blob: 117321ea620c05e75491025cca1d6ad360704aa3 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
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 Moolenaar8f4ac012014-08-10 13:38:34 +020084/* Size of buffer used for encryption. */
85#define CRYPT_BUF_SIZE 8192
86
Bram Moolenaar071d4272004-06-13 20:20:40 +000087#include "vim.h"
88
Bram Moolenaar8f4ac012014-08-10 13:38:34 +020089/* Structure passed around between functions.
90 * Avoids passing cryptstate_T when encryption not available. */
91typedef struct {
92 buf_T *bi_buf;
93 FILE *bi_fp;
94#ifdef FEAT_CRYPT
95 cryptstate_T *bi_state;
96 char_u *bi_buffer; /* CRYPT_BUF_SIZE, NULL when not buffering */
97 size_t bi_used; /* bytes written to/read from bi_buffer */
98 size_t bi_avail; /* bytes available in bi_buffer */
99#endif
100} bufinfo_T;
101
102
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100103static long get_undolevel(void);
104static void u_unch_branch(u_header_T *uhp);
105static u_entry_T *u_get_headentry(void);
106static void u_getbot(void);
107static void u_doit(int count);
108static void u_undoredo(int undo);
109static void u_undo_end(int did_undo, int absolute);
110static void u_add_time(char_u *buf, size_t buflen, time_t tt);
111static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);
112static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);
113static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);
114static void u_freeentry(u_entry_T *, long);
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200115#ifdef FEAT_PERSISTENT_UNDO
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100116static void corruption_error(char *mesg, char_u *file_name);
117static void u_free_uhp(u_header_T *uhp);
118static int undo_write(bufinfo_T *bi, char_u *ptr, size_t len);
Bram Moolenaar1d6fbe62016-02-19 22:46:34 +0100119# ifdef FEAT_CRYPT
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100120static int undo_flush(bufinfo_T *bi);
Bram Moolenaar1d6fbe62016-02-19 22:46:34 +0100121# endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100122static int fwrite_crypt(bufinfo_T *bi, char_u *ptr, size_t len);
123static int undo_write_bytes(bufinfo_T *bi, long_u nr, int len);
124static void put_header_ptr(bufinfo_T *bi, u_header_T *uhp);
125static int undo_read_4c(bufinfo_T *bi);
126static int undo_read_2c(bufinfo_T *bi);
127static int undo_read_byte(bufinfo_T *bi);
128static time_t undo_read_time(bufinfo_T *bi);
129static int undo_read(bufinfo_T *bi, char_u *buffer, size_t size);
130static char_u *read_string_decrypt(bufinfo_T *bi, int len);
131static int serialize_header(bufinfo_T *bi, char_u *hash);
132static int serialize_uhp(bufinfo_T *bi, u_header_T *uhp);
133static u_header_T *unserialize_uhp(bufinfo_T *bi, char_u *file_name);
134static int serialize_uep(bufinfo_T *bi, u_entry_T *uep);
135static u_entry_T *unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name);
136static void serialize_pos(bufinfo_T *bi, pos_T pos);
137static void unserialize_pos(bufinfo_T *bi, pos_T *pos);
138static void serialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);
139static void unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200140#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000141
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200142#define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE)
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100143static char_u *u_save_line(linenr_T);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000144
Bram Moolenaar730cde92010-06-27 05:18:54 +0200145/* used in undo_end() to report number of added and deleted lines */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000146static long u_newcount, u_oldcount;
147
148/*
149 * When 'u' flag included in 'cpoptions', we behave like vi. Need to remember
150 * the action that "u" should do.
151 */
152static int undo_undoes = FALSE;
153
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200154static int lastmark = 0;
155
Bram Moolenaar9db58062010-05-29 20:33:07 +0200156#if defined(U_DEBUG) || defined(PROTO)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000157/*
158 * Check the undo structures for being valid. Print a warning when something
159 * looks wrong.
160 */
161static int seen_b_u_curhead;
162static int seen_b_u_newhead;
163static int header_count;
164
165 static void
166u_check_tree(u_header_T *uhp,
167 u_header_T *exp_uh_next,
168 u_header_T *exp_uh_alt_prev)
169{
170 u_entry_T *uep;
171
172 if (uhp == NULL)
173 return;
174 ++header_count;
175 if (uhp == curbuf->b_u_curhead && ++seen_b_u_curhead > 1)
176 {
177 EMSG("b_u_curhead found twice (looping?)");
178 return;
179 }
180 if (uhp == curbuf->b_u_newhead && ++seen_b_u_newhead > 1)
181 {
182 EMSG("b_u_newhead found twice (looping?)");
183 return;
184 }
185
186 if (uhp->uh_magic != UH_MAGIC)
187 EMSG("uh_magic wrong (may be using freed memory)");
188 else
189 {
190 /* Check pointers back are correct. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200191 if (uhp->uh_next.ptr != exp_uh_next)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000192 {
193 EMSG("uh_next wrong");
194 smsg((char_u *)"expected: 0x%x, actual: 0x%x",
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200195 exp_uh_next, uhp->uh_next.ptr);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000196 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200197 if (uhp->uh_alt_prev.ptr != exp_uh_alt_prev)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000198 {
199 EMSG("uh_alt_prev wrong");
200 smsg((char_u *)"expected: 0x%x, actual: 0x%x",
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200201 exp_uh_alt_prev, uhp->uh_alt_prev.ptr);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000202 }
203
204 /* Check the undo tree at this header. */
205 for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
206 {
207 if (uep->ue_magic != UE_MAGIC)
208 {
209 EMSG("ue_magic wrong (may be using freed memory)");
210 break;
211 }
212 }
213
214 /* Check the next alt tree. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200215 u_check_tree(uhp->uh_alt_next.ptr, uhp->uh_next.ptr, uhp);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000216
217 /* Check the next header in this branch. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200218 u_check_tree(uhp->uh_prev.ptr, uhp, NULL);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000219 }
220}
221
Bram Moolenaarb0b50882010-07-07 18:26:28 +0200222 static void
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000223u_check(int newhead_may_be_NULL)
224{
225 seen_b_u_newhead = 0;
226 seen_b_u_curhead = 0;
227 header_count = 0;
228
229 u_check_tree(curbuf->b_u_oldhead, NULL, NULL);
230
231 if (seen_b_u_newhead == 0 && curbuf->b_u_oldhead != NULL
232 && !(newhead_may_be_NULL && curbuf->b_u_newhead == NULL))
233 EMSGN("b_u_newhead invalid: 0x%x", curbuf->b_u_newhead);
234 if (curbuf->b_u_curhead != NULL && seen_b_u_curhead == 0)
235 EMSGN("b_u_curhead invalid: 0x%x", curbuf->b_u_curhead);
236 if (header_count != curbuf->b_u_numhead)
237 {
238 EMSG("b_u_numhead invalid");
239 smsg((char_u *)"expected: %ld, actual: %ld",
240 (long)header_count, (long)curbuf->b_u_numhead);
241 }
242}
243#endif
244
Bram Moolenaar071d4272004-06-13 20:20:40 +0000245/*
Bram Moolenaard857f0e2005-06-21 22:37:39 +0000246 * Save the current line for both the "u" and "U" command.
Bram Moolenaar687a29c2013-04-15 15:47:12 +0200247 * Careful: may trigger autocommands that reload the buffer.
Bram Moolenaard857f0e2005-06-21 22:37:39 +0000248 * Returns OK or FAIL.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000249 */
250 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100251u_save_cursor(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000252{
253 return (u_save((linenr_T)(curwin->w_cursor.lnum - 1),
254 (linenr_T)(curwin->w_cursor.lnum + 1)));
255}
256
257/*
258 * Save the lines between "top" and "bot" for both the "u" and "U" command.
259 * "top" may be 0 and bot may be curbuf->b_ml.ml_line_count + 1.
Bram Moolenaard04b7502010-07-08 22:27:55 +0200260 * Careful: may trigger autocommands that reload the buffer.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000261 * Returns FAIL when lines could not be saved, OK otherwise.
262 */
263 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100264u_save(linenr_T top, linenr_T bot)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000265{
266 if (undo_off)
267 return OK;
268
Bram Moolenaar687a29c2013-04-15 15:47:12 +0200269 if (top > curbuf->b_ml.ml_line_count
270 || top >= bot
271 || bot > curbuf->b_ml.ml_line_count + 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000272 return FALSE; /* rely on caller to do error messages */
273
274 if (top + 2 == bot)
275 u_saveline((linenr_T)(top + 1));
276
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200277 return (u_savecommon(top, bot, (linenr_T)0, FALSE));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000278}
279
280/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200281 * Save the line "lnum" (used by ":s" and "~" command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000282 * The line is replaced, so the new bottom line is lnum + 1.
Bram Moolenaard04b7502010-07-08 22:27:55 +0200283 * Careful: may trigger autocommands that reload the buffer.
284 * Returns FAIL when lines could not be saved, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000285 */
286 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100287u_savesub(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000288{
289 if (undo_off)
290 return OK;
291
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200292 return (u_savecommon(lnum - 1, lnum + 1, lnum + 1, FALSE));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000293}
294
295/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200296 * A new line is inserted before line "lnum" (used by :s command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000297 * The line is inserted, so the new bottom line is lnum + 1.
Bram Moolenaard04b7502010-07-08 22:27:55 +0200298 * Careful: may trigger autocommands that reload the buffer.
299 * Returns FAIL when lines could not be saved, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000300 */
301 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100302u_inssub(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000303{
304 if (undo_off)
305 return OK;
306
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200307 return (u_savecommon(lnum - 1, lnum, lnum + 1, FALSE));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000308}
309
310/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200311 * Save the lines "lnum" - "lnum" + nlines (used by delete command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000312 * The lines are deleted, so the new bottom line is lnum, unless the buffer
313 * becomes empty.
Bram Moolenaard04b7502010-07-08 22:27:55 +0200314 * Careful: may trigger autocommands that reload the buffer.
315 * Returns FAIL when lines could not be saved, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000316 */
317 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100318u_savedel(linenr_T lnum, long nlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000319{
320 if (undo_off)
321 return OK;
322
323 return (u_savecommon(lnum - 1, lnum + nlines,
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200324 nlines == curbuf->b_ml.ml_line_count ? 2 : lnum, FALSE));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000325}
326
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000327/*
328 * Return TRUE when undo is allowed. Otherwise give an error message and
329 * return FALSE.
330 */
Bram Moolenaarce6ef252006-07-12 19:49:41 +0000331 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100332undo_allowed(void)
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000333{
334 /* Don't allow changes when 'modifiable' is off. */
335 if (!curbuf->b_p_ma)
336 {
337 EMSG(_(e_modifiable));
338 return FALSE;
339 }
340
341#ifdef HAVE_SANDBOX
342 /* In the sandbox it's not allowed to change the text. */
343 if (sandbox != 0)
344 {
345 EMSG(_(e_sandbox));
346 return FALSE;
347 }
348#endif
349
350 /* Don't allow changes in the buffer while editing the cmdline. The
351 * caller of getcmdline() may get confused. */
Bram Moolenaarb71eaae2006-01-20 23:10:18 +0000352 if (textlock != 0)
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000353 {
354 EMSG(_(e_secure));
355 return FALSE;
356 }
357
358 return TRUE;
359}
360
Bram Moolenaarb0b50882010-07-07 18:26:28 +0200361/*
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +0100362 * Get the undolevle value for the current buffer.
363 */
364 static long
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100365get_undolevel(void)
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +0100366{
367 if (curbuf->b_p_ul == NO_LOCAL_UNDOLEVEL)
368 return p_ul;
369 return curbuf->b_p_ul;
370}
371
372/*
Bram Moolenaarb0b50882010-07-07 18:26:28 +0200373 * Common code for various ways to save text before a change.
Bram Moolenaard04b7502010-07-08 22:27:55 +0200374 * "top" is the line above the first changed line.
375 * "bot" is the line below the last changed line.
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200376 * "newbot" is the new bottom line. Use zero when not known.
377 * "reload" is TRUE when saving for a buffer reload.
Bram Moolenaard04b7502010-07-08 22:27:55 +0200378 * Careful: may trigger autocommands that reload the buffer.
379 * Returns FAIL when lines could not be saved, OK otherwise.
Bram Moolenaarb0b50882010-07-07 18:26:28 +0200380 */
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200381 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100382u_savecommon(
383 linenr_T top,
384 linenr_T bot,
385 linenr_T newbot,
386 int reload)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000387{
Bram Moolenaar1e607892006-03-13 22:15:53 +0000388 linenr_T lnum;
389 long i;
390 u_header_T *uhp;
391 u_header_T *old_curhead;
392 u_entry_T *uep;
393 u_entry_T *prev_uep;
394 long size;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000395
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200396 if (!reload)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000397 {
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200398 /* When making changes is not allowed return FAIL. It's a crude way
399 * to make all change commands fail. */
400 if (!undo_allowed())
Bram Moolenaar009b2592004-10-24 19:18:58 +0000401 return FAIL;
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200402
403#ifdef FEAT_NETBEANS_INTG
404 /*
405 * Netbeans defines areas that cannot be modified. Bail out here when
406 * trying to change text in a guarded area.
407 */
408 if (netbeans_active())
Bram Moolenaar009b2592004-10-24 19:18:58 +0000409 {
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200410 if (netbeans_is_guarded(top, bot))
411 {
412 EMSG(_(e_guarded));
413 return FAIL;
414 }
415 if (curbuf->b_p_ro)
416 {
417 EMSG(_(e_nbreadonly));
418 return FAIL;
419 }
Bram Moolenaar009b2592004-10-24 19:18:58 +0000420 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000421#endif
Bram Moolenaar63ecdda2017-07-28 22:29:35 +0200422#ifdef FEAT_TERMINAL
423 /* A change in a terminal buffer removes the highlighting. */
424 term_change_in_curbuf();
425#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000426
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200427 /*
428 * Saving text for undo means we are going to make a change. Give a
429 * warning for a read-only file before making the change, so that the
430 * FileChangedRO event can replace the buffer with a read-write version
431 * (e.g., obtained from a source control system).
432 */
433 change_warning(0);
434 if (bot > curbuf->b_ml.ml_line_count + 1)
435 {
436 /* This happens when the FileChangedRO autocommand changes the
437 * file in a way it becomes shorter. */
Bram Moolenaarb4d587c2014-01-23 18:12:49 +0100438 EMSG(_("E881: Line count changed unexpectedly"));
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200439 return FAIL;
440 }
Bram Moolenaard04b7502010-07-08 22:27:55 +0200441 }
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200442
443#ifdef U_DEBUG
444 u_check(FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000445#endif
446
447 size = bot - top - 1;
448
449 /*
Bram Moolenaarb0b50882010-07-07 18:26:28 +0200450 * If curbuf->b_u_synced == TRUE make a new header.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000451 */
452 if (curbuf->b_u_synced)
453 {
454#ifdef FEAT_JUMPLIST
455 /* Need to create new entry in b_changelist. */
456 curbuf->b_new_change = TRUE;
457#endif
458
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +0100459 if (get_undolevel() >= 0)
Bram Moolenaar1e607892006-03-13 22:15:53 +0000460 {
461 /*
462 * Make a new header entry. Do this first so that we don't mess
463 * up the undo info when out of memory.
464 */
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200465 uhp = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T));
Bram Moolenaar1e607892006-03-13 22:15:53 +0000466 if (uhp == NULL)
467 goto nomem;
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000468#ifdef U_DEBUG
469 uhp->uh_magic = UH_MAGIC;
470#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +0000471 }
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000472 else
473 uhp = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000474
Bram Moolenaar071d4272004-06-13 20:20:40 +0000475 /*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000476 * If we undid more than we redid, move the entry lists before and
477 * including curbuf->b_u_curhead to an alternate branch.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000478 */
Bram Moolenaar1e607892006-03-13 22:15:53 +0000479 old_curhead = curbuf->b_u_curhead;
480 if (old_curhead != NULL)
481 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200482 curbuf->b_u_newhead = old_curhead->uh_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000483 curbuf->b_u_curhead = NULL;
484 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000485
486 /*
487 * free headers to keep the size right
488 */
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +0100489 while (curbuf->b_u_numhead > get_undolevel()
490 && curbuf->b_u_oldhead != NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +0000491 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000492 u_header_T *uhfree = curbuf->b_u_oldhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000493
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000494 if (uhfree == old_curhead)
495 /* Can't reconnect the branch, delete all of it. */
496 u_freebranch(curbuf, uhfree, &old_curhead);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200497 else if (uhfree->uh_alt_next.ptr == NULL)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000498 /* There is no branch, only free one header. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000499 u_freeheader(curbuf, uhfree, &old_curhead);
Bram Moolenaar1e607892006-03-13 22:15:53 +0000500 else
501 {
502 /* Free the oldest alternate branch as a whole. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200503 while (uhfree->uh_alt_next.ptr != NULL)
504 uhfree = uhfree->uh_alt_next.ptr;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000505 u_freebranch(curbuf, uhfree, &old_curhead);
Bram Moolenaar1e607892006-03-13 22:15:53 +0000506 }
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000507#ifdef U_DEBUG
508 u_check(TRUE);
509#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +0000510 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000511
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000512 if (uhp == NULL) /* no undo at all */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000513 {
Bram Moolenaar1e607892006-03-13 22:15:53 +0000514 if (old_curhead != NULL)
515 u_freebranch(curbuf, old_curhead, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000516 curbuf->b_u_synced = FALSE;
517 return OK;
518 }
519
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200520 uhp->uh_prev.ptr = NULL;
521 uhp->uh_next.ptr = curbuf->b_u_newhead;
522 uhp->uh_alt_next.ptr = old_curhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000523 if (old_curhead != NULL)
524 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200525 uhp->uh_alt_prev.ptr = old_curhead->uh_alt_prev.ptr;
526 if (uhp->uh_alt_prev.ptr != NULL)
527 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = uhp;
528 old_curhead->uh_alt_prev.ptr = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000529 if (curbuf->b_u_oldhead == old_curhead)
530 curbuf->b_u_oldhead = uhp;
531 }
Bram Moolenaar89ed3df2007-01-09 19:23:12 +0000532 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200533 uhp->uh_alt_prev.ptr = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000534 if (curbuf->b_u_newhead != NULL)
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200535 curbuf->b_u_newhead->uh_prev.ptr = uhp;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000536
Bram Moolenaarca003e12006-03-17 23:19:38 +0000537 uhp->uh_seq = ++curbuf->b_u_seq_last;
538 curbuf->b_u_seq_cur = uhp->uh_seq;
Bram Moolenaar170b10b2016-07-29 16:15:27 +0200539 uhp->uh_time = vim_time();
Bram Moolenaara800b422010-06-27 01:15:55 +0200540 uhp->uh_save_nr = 0;
541 curbuf->b_u_time_cur = uhp->uh_time + 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000542
Bram Moolenaar1e607892006-03-13 22:15:53 +0000543 uhp->uh_walk = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000544 uhp->uh_entry = NULL;
545 uhp->uh_getbot_entry = NULL;
546 uhp->uh_cursor = curwin->w_cursor; /* save cursor pos. for undo */
547#ifdef FEAT_VIRTUALEDIT
548 if (virtual_active() && curwin->w_cursor.coladd > 0)
549 uhp->uh_cursor_vcol = getviscol();
550 else
551 uhp->uh_cursor_vcol = -1;
552#endif
553
554 /* save changed and buffer empty flag for undo */
555 uhp->uh_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
556 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
557
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000558 /* save named marks and Visual marks for undo */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000559 mch_memmove(uhp->uh_namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000560 uhp->uh_visual = curbuf->b_visual;
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000561
Bram Moolenaar071d4272004-06-13 20:20:40 +0000562 curbuf->b_u_newhead = uhp;
563 if (curbuf->b_u_oldhead == NULL)
564 curbuf->b_u_oldhead = uhp;
565 ++curbuf->b_u_numhead;
566 }
567 else
568 {
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +0100569 if (get_undolevel() < 0) /* no undo at all */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000570 return OK;
571
572 /*
573 * When saving a single line, and it has been saved just before, it
574 * doesn't make sense saving it again. Saves a lot of memory when
575 * making lots of changes inside the same line.
576 * This is only possible if the previous change didn't increase or
577 * decrease the number of lines.
578 * Check the ten last changes. More doesn't make sense and takes too
579 * long.
580 */
581 if (size == 1)
582 {
583 uep = u_get_headentry();
584 prev_uep = NULL;
585 for (i = 0; i < 10; ++i)
586 {
587 if (uep == NULL)
588 break;
589
590 /* If lines have been inserted/deleted we give up.
591 * Also when the line was included in a multi-line save. */
592 if ((curbuf->b_u_newhead->uh_getbot_entry != uep
593 ? (uep->ue_top + uep->ue_size + 1
594 != (uep->ue_bot == 0
595 ? curbuf->b_ml.ml_line_count + 1
596 : uep->ue_bot))
597 : uep->ue_lcount != curbuf->b_ml.ml_line_count)
598 || (uep->ue_size > 1
599 && top >= uep->ue_top
600 && top + 2 <= uep->ue_top + uep->ue_size + 1))
601 break;
602
603 /* If it's the same line we can skip saving it again. */
604 if (uep->ue_size == 1 && uep->ue_top == top)
605 {
606 if (i > 0)
607 {
608 /* It's not the last entry: get ue_bot for the last
609 * entry now. Following deleted/inserted lines go to
610 * the re-used entry. */
611 u_getbot();
612 curbuf->b_u_synced = FALSE;
613
614 /* Move the found entry to become the last entry. The
615 * order of undo/redo doesn't matter for the entries
616 * we move it over, since they don't change the line
617 * count and don't include this line. It does matter
618 * for the found entry if the line count is changed by
619 * the executed command. */
620 prev_uep->ue_next = uep->ue_next;
621 uep->ue_next = curbuf->b_u_newhead->uh_entry;
622 curbuf->b_u_newhead->uh_entry = uep;
623 }
624
625 /* The executed command may change the line count. */
626 if (newbot != 0)
627 uep->ue_bot = newbot;
628 else if (bot > curbuf->b_ml.ml_line_count)
629 uep->ue_bot = 0;
630 else
631 {
632 uep->ue_lcount = curbuf->b_ml.ml_line_count;
633 curbuf->b_u_newhead->uh_getbot_entry = uep;
634 }
635 return OK;
636 }
637 prev_uep = uep;
638 uep = uep->ue_next;
639 }
640 }
641
642 /* find line number for ue_bot for previous u_save() */
643 u_getbot();
644 }
645
Bram Moolenaara06ecab2016-07-16 14:47:36 +0200646#if !defined(UNIX) && !defined(WIN32)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000647 /*
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100648 * With Amiga we can't handle big undo's, because
Bram Moolenaar071d4272004-06-13 20:20:40 +0000649 * then u_alloc_line would have to allocate a block larger than 32K
650 */
651 if (size >= 8000)
652 goto nomem;
653#endif
654
655 /*
656 * add lines in front of entry list
657 */
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200658 uep = (u_entry_T *)U_ALLOC_LINE(sizeof(u_entry_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000659 if (uep == NULL)
660 goto nomem;
Bram Moolenaar7db5fc82010-05-24 11:59:29 +0200661 vim_memset(uep, 0, sizeof(u_entry_T));
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000662#ifdef U_DEBUG
663 uep->ue_magic = UE_MAGIC;
664#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000665
666 uep->ue_size = size;
667 uep->ue_top = top;
668 if (newbot != 0)
669 uep->ue_bot = newbot;
670 /*
671 * Use 0 for ue_bot if bot is below last line.
672 * Otherwise we have to compute ue_bot later.
673 */
674 else if (bot > curbuf->b_ml.ml_line_count)
675 uep->ue_bot = 0;
676 else
677 {
678 uep->ue_lcount = curbuf->b_ml.ml_line_count;
679 curbuf->b_u_newhead->uh_getbot_entry = uep;
680 }
681
Bram Moolenaar26a60b42005-02-22 08:49:11 +0000682 if (size > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000683 {
Bram Moolenaar26a60b42005-02-22 08:49:11 +0000684 if ((uep->ue_array = (char_u **)U_ALLOC_LINE(
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200685 sizeof(char_u *) * size)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000686 {
687 u_freeentry(uep, 0L);
688 goto nomem;
689 }
690 for (i = 0, lnum = top + 1; i < size; ++i)
691 {
Bram Moolenaarae5bce12005-08-15 21:41:48 +0000692 fast_breakcheck();
693 if (got_int)
694 {
695 u_freeentry(uep, i);
696 return FAIL;
697 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000698 if ((uep->ue_array[i] = u_save_line(lnum++)) == NULL)
699 {
700 u_freeentry(uep, i);
701 goto nomem;
702 }
703 }
704 }
Bram Moolenaarf461c8e2005-06-25 23:04:51 +0000705 else
706 uep->ue_array = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000707 uep->ue_next = curbuf->b_u_newhead->uh_entry;
708 curbuf->b_u_newhead->uh_entry = uep;
709 curbuf->b_u_synced = FALSE;
710 undo_undoes = FALSE;
711
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000712#ifdef U_DEBUG
713 u_check(FALSE);
714#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000715 return OK;
716
717nomem:
718 msg_silent = 0; /* must display the prompt */
719 if (ask_yesno((char_u *)_("No undo possible; continue anyway"), TRUE)
720 == 'y')
721 {
722 undo_off = TRUE; /* will be reset when character typed */
723 return OK;
724 }
725 do_outofmem_msg((long_u)0);
726 return FAIL;
727}
728
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200729#if defined(FEAT_PERSISTENT_UNDO) || defined(PROTO)
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200730
Bram Moolenaar9db58062010-05-29 20:33:07 +0200731# define UF_START_MAGIC "Vim\237UnDo\345" /* magic at start of undofile */
732# define UF_START_MAGIC_LEN 9
733# define UF_HEADER_MAGIC 0x5fd0 /* magic at start of header */
734# define UF_HEADER_END_MAGIC 0xe7aa /* magic after last header */
735# define UF_ENTRY_MAGIC 0xf518 /* magic at start of entry */
736# define UF_ENTRY_END_MAGIC 0x3581 /* magic after last entry */
Bram Moolenaara800b422010-06-27 01:15:55 +0200737# define UF_VERSION 2 /* 2-byte undofile version number */
Bram Moolenaara800b422010-06-27 01:15:55 +0200738# define UF_VERSION_CRYPT 0x8002 /* idem, encrypted */
739
740/* extra fields for header */
741# define UF_LAST_SAVE_NR 1
742
743/* extra fields for uhp */
744# define UHP_SAVE_NR 1
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200745
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200746static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200747
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200748/*
749 * Compute the hash for the current buffer text into hash[UNDO_HASH_SIZE].
750 */
751 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100752u_compute_hash(char_u *hash)
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200753{
754 context_sha256_T ctx;
755 linenr_T lnum;
756 char_u *p;
757
758 sha256_start(&ctx);
Bram Moolenaarae7ba982011-12-08 15:14:09 +0100759 for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum)
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200760 {
761 p = ml_get(lnum);
Bram Moolenaar442b4222010-05-24 21:34:22 +0200762 sha256_update(&ctx, p, (UINT32_T)(STRLEN(p) + 1));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200763 }
764 sha256_finish(&ctx, hash);
765}
766
767/*
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200768 * Return an allocated string of the full path of the target undofile.
769 * When "reading" is TRUE find the file to read, go over all directories in
770 * 'undodir'.
771 * When "reading" is FALSE use the first name where the directory exists.
Bram Moolenaar9db58062010-05-29 20:33:07 +0200772 * Returns NULL when there is no place to write or no file to read.
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200773 */
Bram Moolenaara17d4c12010-05-30 18:30:36 +0200774 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100775u_get_undo_file_name(char_u *buf_ffname, int reading)
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200776{
777 char_u *dirp;
778 char_u dir_name[IOSIZE + 1];
779 char_u *munged_name = NULL;
780 char_u *undo_file_name = NULL;
781 int dir_len;
782 char_u *p;
Bram Moolenaar8767f522016-07-01 17:17:39 +0200783 stat_T st;
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200784 char_u *ffname = buf_ffname;
785#ifdef HAVE_READLINK
786 char_u fname_buf[MAXPATHL];
787#endif
788
789 if (ffname == NULL)
790 return NULL;
791
792#ifdef HAVE_READLINK
793 /* Expand symlink in the file name, so that we put the undo file with the
794 * actual file instead of with the symlink. */
795 if (resolve_symlink(ffname, fname_buf) == OK)
796 ffname = fname_buf;
797#endif
798
799 /* Loop over 'undodir'. When reading find the first file that exists.
800 * When not reading use the first directory that exists or ".". */
801 dirp = p_udir;
802 while (*dirp != NUL)
803 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +0200804 dir_len = copy_option_part(&dirp, dir_name, IOSIZE, ",");
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200805 if (dir_len == 1 && dir_name[0] == '.')
806 {
807 /* Use same directory as the ffname,
808 * "dir/name" -> "dir/.name.un~" */
Bram Moolenaar442b4222010-05-24 21:34:22 +0200809 undo_file_name = vim_strnsave(ffname, (int)(STRLEN(ffname) + 5));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200810 if (undo_file_name == NULL)
811 break;
812 p = gettail(undo_file_name);
Bram Moolenaar206f0112014-03-12 16:51:55 +0100813#ifdef VMS
814 /* VMS can not handle more than one dot in the filenames
815 * use "dir/name" -> "dir/_un_name" - add _un_
816 * at the beginning to keep the extension */
817 mch_memmove(p + 4, p, STRLEN(p) + 1);
818 mch_memmove(p, "_un_", 4);
819
820#else
821 /* Use same directory as the ffname,
822 * "dir/name" -> "dir/.name.un~" */
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200823 mch_memmove(p + 1, p, STRLEN(p) + 1);
824 *p = '.';
825 STRCAT(p, ".un~");
Bram Moolenaar206f0112014-03-12 16:51:55 +0100826#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200827 }
828 else
829 {
830 dir_name[dir_len] = NUL;
831 if (mch_isdir(dir_name))
832 {
833 if (munged_name == NULL)
834 {
835 munged_name = vim_strsave(ffname);
836 if (munged_name == NULL)
837 return NULL;
Bram Moolenaar91acfff2017-03-12 19:22:36 +0100838 for (p = munged_name; *p != NUL; MB_PTR_ADV(p))
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200839 if (vim_ispathsep(*p))
840 *p = '%';
841 }
842 undo_file_name = concat_fnames(dir_name, munged_name, TRUE);
843 }
844 }
845
846 /* When reading check if the file exists. */
847 if (undo_file_name != NULL && (!reading
848 || mch_stat((char *)undo_file_name, &st) >= 0))
849 break;
Bram Moolenaard23a8232018-02-10 18:45:26 +0100850 VIM_CLEAR(undo_file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200851 }
852
853 vim_free(munged_name);
854 return undo_file_name;
855}
856
Bram Moolenaar9db58062010-05-29 20:33:07 +0200857 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100858corruption_error(char *mesg, char_u *file_name)
Bram Moolenaar9db58062010-05-29 20:33:07 +0200859{
Bram Moolenaarf506c5b2010-06-22 06:28:58 +0200860 EMSG3(_("E825: Corrupted undo file (%s): %s"), mesg, file_name);
Bram Moolenaar9db58062010-05-29 20:33:07 +0200861}
862
863 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100864u_free_uhp(u_header_T *uhp)
Bram Moolenaar9db58062010-05-29 20:33:07 +0200865{
866 u_entry_T *nuep;
867 u_entry_T *uep;
868
869 uep = uhp->uh_entry;
870 while (uep != NULL)
871 {
872 nuep = uep->ue_next;
873 u_freeentry(uep, uep->ue_size);
874 uep = nuep;
875 }
876 vim_free(uhp);
877}
878
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200879/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200880 * Write a sequence of bytes to the undo file.
881 * Buffers and encrypts as needed.
882 * Returns OK or FAIL.
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200883 */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200884 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100885undo_write(bufinfo_T *bi, char_u *ptr, size_t len)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200886{
887#ifdef FEAT_CRYPT
888 if (bi->bi_buffer != NULL)
889 {
890 size_t len_todo = len;
891 char_u *p = ptr;
892
893 while (bi->bi_used + len_todo >= CRYPT_BUF_SIZE)
894 {
895 size_t n = CRYPT_BUF_SIZE - bi->bi_used;
896
897 mch_memmove(bi->bi_buffer + bi->bi_used, p, n);
898 len_todo -= n;
899 p += n;
900 bi->bi_used = CRYPT_BUF_SIZE;
901 if (undo_flush(bi) == FAIL)
902 return FAIL;
903 }
904 if (len_todo > 0)
905 {
906 mch_memmove(bi->bi_buffer + bi->bi_used, p, len_todo);
907 bi->bi_used += len_todo;
908 }
909 return OK;
910 }
911#endif
912 if (fwrite(ptr, len, (size_t)1, bi->bi_fp) != 1)
913 return FAIL;
914 return OK;
915}
916
917#ifdef FEAT_CRYPT
918 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100919undo_flush(bufinfo_T *bi)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200920{
Bram Moolenaar829aa642017-08-23 22:32:35 +0200921 if (bi->bi_buffer != NULL && bi->bi_state != NULL && bi->bi_used > 0)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200922 {
923 crypt_encode_inplace(bi->bi_state, bi->bi_buffer, bi->bi_used);
924 if (fwrite(bi->bi_buffer, bi->bi_used, (size_t)1, bi->bi_fp) != 1)
925 return FAIL;
926 bi->bi_used = 0;
927 }
928 return OK;
929}
930#endif
931
932/*
933 * Write "ptr[len]" and crypt the bytes when needed.
934 * Returns OK or FAIL.
935 */
936 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100937fwrite_crypt(bufinfo_T *bi, char_u *ptr, size_t len)
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200938{
939#ifdef FEAT_CRYPT
940 char_u *copy;
941 char_u small_buf[100];
942 size_t i;
943
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200944 if (bi->bi_state != NULL && bi->bi_buffer == NULL)
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200945 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200946 /* crypting every piece of text separately */
947 if (len < 100)
948 copy = small_buf; /* no malloc()/free() for short strings */
949 else
950 {
951 copy = lalloc(len, FALSE);
952 if (copy == NULL)
953 return 0;
954 }
955 crypt_encode(bi->bi_state, ptr, len, copy);
956 i = fwrite(copy, len, (size_t)1, bi->bi_fp);
957 if (copy != small_buf)
958 vim_free(copy);
959 return i == 1 ? OK : FAIL;
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200960 }
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200961#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200962 return undo_write(bi, ptr, len);
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200963}
964
965/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200966 * Write a number, MSB first, in "len" bytes.
967 * Must match with undo_read_?c() functions.
968 * Returns OK or FAIL.
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200969 */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200970 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100971undo_write_bytes(bufinfo_T *bi, long_u nr, int len)
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200972{
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200973 char_u buf[8];
974 int i;
975 int bufi = 0;
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200976
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200977 for (i = len - 1; i >= 0; --i)
Bram Moolenaar9b8f0212014-08-13 22:05:53 +0200978 buf[bufi++] = (char_u)(nr >> (i * 8));
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200979 return undo_write(bi, buf, (size_t)len);
980}
981
982/*
983 * Write the pointer to an undo header. Instead of writing the pointer itself
984 * we use the sequence number of the header. This is converted back to
985 * pointers when reading. */
986 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100987put_header_ptr(bufinfo_T *bi, u_header_T *uhp)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200988{
989 undo_write_bytes(bi, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200990}
991
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +0200992 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100993undo_read_4c(bufinfo_T *bi)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200994{
995#ifdef FEAT_CRYPT
996 if (bi->bi_buffer != NULL)
997 {
998 char_u buf[4];
999 int n;
1000
1001 undo_read(bi, buf, (size_t)4);
Bram Moolenaar35169282014-09-11 22:50:09 +02001002 n = ((unsigned)buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3];
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001003 return n;
1004 }
1005#endif
1006 return get4c(bi->bi_fp);
1007}
1008
1009 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001010undo_read_2c(bufinfo_T *bi)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001011{
1012#ifdef FEAT_CRYPT
1013 if (bi->bi_buffer != NULL)
1014 {
1015 char_u buf[2];
1016 int n;
1017
1018 undo_read(bi, buf, (size_t)2);
1019 n = (buf[0] << 8) + buf[1];
1020 return n;
1021 }
1022#endif
1023 return get2c(bi->bi_fp);
1024}
1025
1026 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001027undo_read_byte(bufinfo_T *bi)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001028{
1029#ifdef FEAT_CRYPT
1030 if (bi->bi_buffer != NULL)
1031 {
1032 char_u buf[1];
1033
1034 undo_read(bi, buf, (size_t)1);
1035 return buf[0];
1036 }
1037#endif
1038 return getc(bi->bi_fp);
1039}
1040
1041 static time_t
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001042undo_read_time(bufinfo_T *bi)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001043{
1044#ifdef FEAT_CRYPT
1045 if (bi->bi_buffer != NULL)
1046 {
1047 char_u buf[8];
1048 time_t n = 0;
1049 int i;
1050
1051 undo_read(bi, buf, (size_t)8);
1052 for (i = 0; i < 8; ++i)
1053 n = (n << 8) + buf[i];
1054 return n;
1055 }
1056#endif
1057 return get8ctime(bi->bi_fp);
1058}
1059
1060/*
1061 * Read "buffer[size]" from the undo file.
1062 * Return OK or FAIL.
1063 */
1064 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001065undo_read(bufinfo_T *bi, char_u *buffer, size_t size)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001066{
Bram Moolenaar56f2db52017-06-11 23:09:15 +02001067 int retval = OK;
1068
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001069#ifdef FEAT_CRYPT
1070 if (bi->bi_buffer != NULL)
1071 {
Bram Moolenaar9b8f0212014-08-13 22:05:53 +02001072 int size_todo = (int)size;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001073 char_u *p = buffer;
1074
1075 while (size_todo > 0)
1076 {
1077 size_t n;
1078
1079 if (bi->bi_used >= bi->bi_avail)
1080 {
1081 n = fread(bi->bi_buffer, 1, (size_t)CRYPT_BUF_SIZE, bi->bi_fp);
Bram Moolenaar55952d42016-11-05 14:58:34 +01001082 if (n == 0)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001083 {
Bram Moolenaar56f2db52017-06-11 23:09:15 +02001084 retval = FAIL;
1085 break;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001086 }
1087 bi->bi_avail = n;
1088 bi->bi_used = 0;
1089 crypt_decode_inplace(bi->bi_state, bi->bi_buffer, bi->bi_avail);
1090 }
1091 n = size_todo;
1092 if (n > bi->bi_avail - bi->bi_used)
1093 n = bi->bi_avail - bi->bi_used;
1094 mch_memmove(p, bi->bi_buffer + bi->bi_used, n);
1095 bi->bi_used += n;
Bram Moolenaar9b8f0212014-08-13 22:05:53 +02001096 size_todo -= (int)n;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001097 p += n;
1098 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001099 }
Bram Moolenaar56f2db52017-06-11 23:09:15 +02001100 else
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001101#endif
1102 if (fread(buffer, (size_t)size, 1, bi->bi_fp) != 1)
Bram Moolenaar56f2db52017-06-11 23:09:15 +02001103 retval = FAIL;
1104
1105 if (retval == FAIL)
1106 /* Error may be checked for only later. Fill with zeros,
1107 * so that the reader won't use garbage. */
1108 vim_memset(buffer, 0, size);
1109 return retval;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001110}
1111
1112/*
1113 * Read a string of length "len" from "bi->bi_fd".
1114 * "len" can be zero to allocate an empty line.
1115 * Decrypt the bytes if needed.
1116 * Append a NUL.
1117 * Returns a pointer to allocated memory or NULL for failure.
1118 */
1119 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001120read_string_decrypt(bufinfo_T *bi, int len)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001121{
1122 char_u *ptr = alloc((unsigned)len + 1);
1123
1124 if (ptr != NULL)
1125 {
1126 if (len > 0 && undo_read(bi, ptr, len) == FAIL)
1127 {
1128 vim_free(ptr);
1129 return NULL;
1130 }
1131 ptr[len] = NUL;
1132#ifdef FEAT_CRYPT
1133 if (bi->bi_state != NULL && bi->bi_buffer == NULL)
1134 crypt_decode_inplace(bi->bi_state, ptr, len);
1135#endif
1136 }
1137 return ptr;
1138}
1139
1140/*
1141 * Writes the (not encrypted) header and initializes encryption if needed.
1142 */
1143 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001144serialize_header(bufinfo_T *bi, char_u *hash)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001145{
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001146 int len;
1147 buf_T *buf = bi->bi_buf;
1148 FILE *fp = bi->bi_fp;
1149 char_u time_buf[8];
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001150
1151 /* Start writing, first the magic marker and undo info version. */
1152 if (fwrite(UF_START_MAGIC, (size_t)UF_START_MAGIC_LEN, (size_t)1, fp) != 1)
1153 return FAIL;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001154
1155 /* If the buffer is encrypted then all text bytes following will be
1156 * encrypted. Numbers and other info is not crypted. */
1157#ifdef FEAT_CRYPT
Bram Moolenaarfa0ff9a2010-07-25 16:05:19 +02001158 if (*buf->b_p_key != NUL)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001159 {
1160 char_u *header;
1161 int header_len;
1162
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001163 undo_write_bytes(bi, (long_u)UF_VERSION_CRYPT, 2);
1164 bi->bi_state = crypt_create_for_writing(crypt_get_method_nr(buf),
1165 buf->b_p_key, &header, &header_len);
1166 if (bi->bi_state == NULL)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001167 return FAIL;
Bram Moolenaarbbd6afe2010-06-02 20:32:23 +02001168 len = (int)fwrite(header, (size_t)header_len, (size_t)1, fp);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001169 vim_free(header);
1170 if (len != 1)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001171 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001172 crypt_free_state(bi->bi_state);
1173 bi->bi_state = NULL;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001174 return FAIL;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001175 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001176
1177 if (crypt_whole_undofile(crypt_get_method_nr(buf)))
1178 {
1179 bi->bi_buffer = alloc(CRYPT_BUF_SIZE);
1180 if (bi->bi_buffer == NULL)
1181 {
1182 crypt_free_state(bi->bi_state);
1183 bi->bi_state = NULL;
1184 return FAIL;
1185 }
1186 bi->bi_used = 0;
1187 }
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001188 }
1189 else
1190#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001191 undo_write_bytes(bi, (long_u)UF_VERSION, 2);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001192
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001193
1194 /* Write a hash of the buffer text, so that we can verify it is still the
1195 * same when reading the buffer text. */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001196 if (undo_write(bi, hash, (size_t)UNDO_HASH_SIZE) == FAIL)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001197 return FAIL;
1198
1199 /* buffer-specific data */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001200 undo_write_bytes(bi, (long_u)buf->b_ml.ml_line_count, 4);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001201 len = buf->b_u_line_ptr != NULL ? (int)STRLEN(buf->b_u_line_ptr) : 0;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001202 undo_write_bytes(bi, (long_u)len, 4);
1203 if (len > 0 && fwrite_crypt(bi, buf->b_u_line_ptr, (size_t)len) == FAIL)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001204 return FAIL;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001205 undo_write_bytes(bi, (long_u)buf->b_u_line_lnum, 4);
1206 undo_write_bytes(bi, (long_u)buf->b_u_line_colnr, 4);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001207
1208 /* Undo structures header data */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001209 put_header_ptr(bi, buf->b_u_oldhead);
1210 put_header_ptr(bi, buf->b_u_newhead);
1211 put_header_ptr(bi, buf->b_u_curhead);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001212
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001213 undo_write_bytes(bi, (long_u)buf->b_u_numhead, 4);
1214 undo_write_bytes(bi, (long_u)buf->b_u_seq_last, 4);
1215 undo_write_bytes(bi, (long_u)buf->b_u_seq_cur, 4);
1216 time_to_bytes(buf->b_u_time_cur, time_buf);
1217 undo_write(bi, time_buf, 8);
Bram Moolenaara800b422010-06-27 01:15:55 +02001218
1219 /* Optional fields. */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001220 undo_write_bytes(bi, 4, 1);
1221 undo_write_bytes(bi, UF_LAST_SAVE_NR, 1);
1222 undo_write_bytes(bi, (long_u)buf->b_u_save_nr_last, 4);
Bram Moolenaara800b422010-06-27 01:15:55 +02001223
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001224 undo_write_bytes(bi, 0, 1); /* end marker */
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001225
1226 return OK;
1227}
1228
1229 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001230serialize_uhp(bufinfo_T *bi, u_header_T *uhp)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001231{
1232 int i;
1233 u_entry_T *uep;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001234 char_u time_buf[8];
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001235
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001236 if (undo_write_bytes(bi, (long_u)UF_HEADER_MAGIC, 2) == FAIL)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001237 return FAIL;
1238
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001239 put_header_ptr(bi, uhp->uh_next.ptr);
1240 put_header_ptr(bi, uhp->uh_prev.ptr);
1241 put_header_ptr(bi, uhp->uh_alt_next.ptr);
1242 put_header_ptr(bi, uhp->uh_alt_prev.ptr);
1243 undo_write_bytes(bi, uhp->uh_seq, 4);
1244 serialize_pos(bi, uhp->uh_cursor);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001245#ifdef FEAT_VIRTUALEDIT
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001246 undo_write_bytes(bi, (long_u)uhp->uh_cursor_vcol, 4);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001247#else
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001248 undo_write_bytes(bi, (long_u)0, 4);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001249#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001250 undo_write_bytes(bi, (long_u)uhp->uh_flags, 2);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001251 /* Assume NMARKS will stay the same. */
1252 for (i = 0; i < NMARKS; ++i)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001253 serialize_pos(bi, uhp->uh_namedm[i]);
1254 serialize_visualinfo(bi, &uhp->uh_visual);
1255 time_to_bytes(uhp->uh_time, time_buf);
1256 undo_write(bi, time_buf, 8);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001257
Bram Moolenaara800b422010-06-27 01:15:55 +02001258 /* Optional fields. */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001259 undo_write_bytes(bi, 4, 1);
1260 undo_write_bytes(bi, UHP_SAVE_NR, 1);
1261 undo_write_bytes(bi, (long_u)uhp->uh_save_nr, 4);
Bram Moolenaara800b422010-06-27 01:15:55 +02001262
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001263 undo_write_bytes(bi, 0, 1); /* end marker */
Bram Moolenaara800b422010-06-27 01:15:55 +02001264
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001265 /* Write all the entries. */
1266 for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
1267 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001268 undo_write_bytes(bi, (long_u)UF_ENTRY_MAGIC, 2);
1269 if (serialize_uep(bi, uep) == FAIL)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001270 return FAIL;
1271 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001272 undo_write_bytes(bi, (long_u)UF_ENTRY_END_MAGIC, 2);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001273 return OK;
1274}
1275
1276 static u_header_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001277unserialize_uhp(bufinfo_T *bi, char_u *file_name)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001278{
1279 u_header_T *uhp;
1280 int i;
1281 u_entry_T *uep, *last_uep;
1282 int c;
1283 int error;
1284
1285 uhp = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T));
1286 if (uhp == NULL)
1287 return NULL;
1288 vim_memset(uhp, 0, sizeof(u_header_T));
1289#ifdef U_DEBUG
1290 uhp->uh_magic = UH_MAGIC;
1291#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001292 uhp->uh_next.seq = undo_read_4c(bi);
1293 uhp->uh_prev.seq = undo_read_4c(bi);
1294 uhp->uh_alt_next.seq = undo_read_4c(bi);
1295 uhp->uh_alt_prev.seq = undo_read_4c(bi);
1296 uhp->uh_seq = undo_read_4c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001297 if (uhp->uh_seq <= 0)
1298 {
1299 corruption_error("uh_seq", file_name);
1300 vim_free(uhp);
1301 return NULL;
1302 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001303 unserialize_pos(bi, &uhp->uh_cursor);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001304#ifdef FEAT_VIRTUALEDIT
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001305 uhp->uh_cursor_vcol = undo_read_4c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001306#else
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001307 (void)undo_read_4c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001308#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001309 uhp->uh_flags = undo_read_2c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001310 for (i = 0; i < NMARKS; ++i)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001311 unserialize_pos(bi, &uhp->uh_namedm[i]);
1312 unserialize_visualinfo(bi, &uhp->uh_visual);
1313 uhp->uh_time = undo_read_time(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001314
Bram Moolenaara800b422010-06-27 01:15:55 +02001315 /* Optional fields. */
Bram Moolenaar69154f22010-07-18 21:42:34 +02001316 for (;;)
1317 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001318 int len = undo_read_byte(bi);
Bram Moolenaar69154f22010-07-18 21:42:34 +02001319 int what;
Bram Moolenaara800b422010-06-27 01:15:55 +02001320
Bram Moolenaar69154f22010-07-18 21:42:34 +02001321 if (len == 0)
1322 break;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001323 what = undo_read_byte(bi);
Bram Moolenaar69154f22010-07-18 21:42:34 +02001324 switch (what)
1325 {
1326 case UHP_SAVE_NR:
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001327 uhp->uh_save_nr = undo_read_4c(bi);
Bram Moolenaara800b422010-06-27 01:15:55 +02001328 break;
Bram Moolenaar69154f22010-07-18 21:42:34 +02001329 default:
1330 /* field not supported, skip */
1331 while (--len >= 0)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001332 (void)undo_read_byte(bi);
Bram Moolenaara800b422010-06-27 01:15:55 +02001333 }
Bram Moolenaar69154f22010-07-18 21:42:34 +02001334 }
Bram Moolenaara800b422010-06-27 01:15:55 +02001335
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001336 /* Unserialize the uep list. */
1337 last_uep = NULL;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001338 while ((c = undo_read_2c(bi)) == UF_ENTRY_MAGIC)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001339 {
1340 error = FALSE;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001341 uep = unserialize_uep(bi, &error, file_name);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001342 if (last_uep == NULL)
1343 uhp->uh_entry = uep;
1344 else
1345 last_uep->ue_next = uep;
1346 last_uep = uep;
1347 if (uep == NULL || error)
1348 {
1349 u_free_uhp(uhp);
1350 return NULL;
1351 }
1352 }
1353 if (c != UF_ENTRY_END_MAGIC)
1354 {
1355 corruption_error("entry end", file_name);
1356 u_free_uhp(uhp);
1357 return NULL;
1358 }
1359
1360 return uhp;
1361}
1362
Bram Moolenaar9db58062010-05-29 20:33:07 +02001363/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001364 * Serialize "uep".
Bram Moolenaar9db58062010-05-29 20:33:07 +02001365 */
1366 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001367serialize_uep(
1368 bufinfo_T *bi,
1369 u_entry_T *uep)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001370{
1371 int i;
1372 size_t len;
1373
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001374 undo_write_bytes(bi, (long_u)uep->ue_top, 4);
1375 undo_write_bytes(bi, (long_u)uep->ue_bot, 4);
1376 undo_write_bytes(bi, (long_u)uep->ue_lcount, 4);
1377 undo_write_bytes(bi, (long_u)uep->ue_size, 4);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001378 for (i = 0; i < uep->ue_size; ++i)
1379 {
1380 len = STRLEN(uep->ue_array[i]);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001381 if (undo_write_bytes(bi, (long_u)len, 4) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001382 return FAIL;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001383 if (len > 0 && fwrite_crypt(bi, uep->ue_array[i], len) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001384 return FAIL;
1385 }
1386 return OK;
1387}
1388
1389 static u_entry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001390unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001391{
1392 int i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001393 u_entry_T *uep;
Bram Moolenaar0c8485f2017-02-26 18:17:10 +01001394 char_u **array = NULL;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001395 char_u *line;
1396 int line_len;
1397
1398 uep = (u_entry_T *)U_ALLOC_LINE(sizeof(u_entry_T));
1399 if (uep == NULL)
1400 return NULL;
1401 vim_memset(uep, 0, sizeof(u_entry_T));
1402#ifdef U_DEBUG
1403 uep->ue_magic = UE_MAGIC;
1404#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001405 uep->ue_top = undo_read_4c(bi);
1406 uep->ue_bot = undo_read_4c(bi);
1407 uep->ue_lcount = undo_read_4c(bi);
1408 uep->ue_size = undo_read_4c(bi);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001409 if (uep->ue_size > 0)
1410 {
Bram Moolenaar0c8485f2017-02-26 18:17:10 +01001411 if (uep->ue_size < LONG_MAX / (int)sizeof(char_u *))
1412 array = (char_u **)U_ALLOC_LINE(sizeof(char_u *) * uep->ue_size);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001413 if (array == NULL)
1414 {
1415 *error = TRUE;
1416 return uep;
1417 }
1418 vim_memset(array, 0, sizeof(char_u *) * uep->ue_size);
1419 }
1420 uep->ue_array = array;
1421
1422 for (i = 0; i < uep->ue_size; ++i)
1423 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001424 line_len = undo_read_4c(bi);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001425 if (line_len >= 0)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001426 line = read_string_decrypt(bi, line_len);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001427 else
1428 {
1429 line = NULL;
1430 corruption_error("line length", file_name);
1431 }
1432 if (line == NULL)
1433 {
1434 *error = TRUE;
1435 return uep;
1436 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001437 array[i] = line;
1438 }
1439 return uep;
1440}
1441
1442/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001443 * Serialize "pos".
Bram Moolenaar9db58062010-05-29 20:33:07 +02001444 */
1445 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001446serialize_pos(bufinfo_T *bi, pos_T pos)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001447{
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001448 undo_write_bytes(bi, (long_u)pos.lnum, 4);
1449 undo_write_bytes(bi, (long_u)pos.col, 4);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001450#ifdef FEAT_VIRTUALEDIT
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001451 undo_write_bytes(bi, (long_u)pos.coladd, 4);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001452#else
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001453 undo_write_bytes(bi, (long_u)0, 4);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001454#endif
1455}
1456
1457/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001458 * Unserialize the pos_T at the current position.
Bram Moolenaar9db58062010-05-29 20:33:07 +02001459 */
1460 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001461unserialize_pos(bufinfo_T *bi, pos_T *pos)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001462{
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001463 pos->lnum = undo_read_4c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001464 if (pos->lnum < 0)
1465 pos->lnum = 0;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001466 pos->col = undo_read_4c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001467 if (pos->col < 0)
1468 pos->col = 0;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001469#ifdef FEAT_VIRTUALEDIT
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001470 pos->coladd = undo_read_4c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001471 if (pos->coladd < 0)
1472 pos->coladd = 0;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001473#else
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001474 (void)undo_read_4c(bi);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001475#endif
1476}
1477
1478/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001479 * Serialize "info".
Bram Moolenaar9db58062010-05-29 20:33:07 +02001480 */
1481 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001482serialize_visualinfo(bufinfo_T *bi, visualinfo_T *info)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001483{
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001484 serialize_pos(bi, info->vi_start);
1485 serialize_pos(bi, info->vi_end);
1486 undo_write_bytes(bi, (long_u)info->vi_mode, 4);
1487 undo_write_bytes(bi, (long_u)info->vi_curswant, 4);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001488}
1489
1490/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001491 * Unserialize the visualinfo_T at the current position.
Bram Moolenaar9db58062010-05-29 20:33:07 +02001492 */
1493 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001494unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001495{
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001496 unserialize_pos(bi, &info->vi_start);
1497 unserialize_pos(bi, &info->vi_end);
1498 info->vi_mode = undo_read_4c(bi);
1499 info->vi_curswant = undo_read_4c(bi);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001500}
1501
1502/*
1503 * Write the undo tree in an undo file.
1504 * When "name" is not NULL, use it as the name of the undo file.
1505 * Otherwise use buf->b_ffname to generate the undo file name.
1506 * "buf" must never be null, buf->b_ffname is used to obtain the original file
1507 * permissions.
1508 * "forceit" is TRUE for ":wundo!", FALSE otherwise.
1509 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
1510 */
1511 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001512u_write_undo(
1513 char_u *name,
1514 int forceit,
1515 buf_T *buf,
1516 char_u *hash)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001517{
1518 u_header_T *uhp;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001519 char_u *file_name;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001520 int mark;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001521#ifdef U_DEBUG
1522 int headers_written = 0;
1523#endif
1524 int fd;
1525 FILE *fp = NULL;
1526 int perm;
1527 int write_ok = FALSE;
1528#ifdef UNIX
1529 int st_old_valid = FALSE;
Bram Moolenaar8767f522016-07-01 17:17:39 +02001530 stat_T st_old;
1531 stat_T st_new;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001532#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001533 bufinfo_T bi;
1534
Bram Moolenaar5a669b92014-08-12 20:14:33 +02001535 vim_memset(&bi, 0, sizeof(bi));
Bram Moolenaar9db58062010-05-29 20:33:07 +02001536
1537 if (name == NULL)
1538 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001539 file_name = u_get_undo_file_name(buf->b_ffname, FALSE);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001540 if (file_name == NULL)
1541 {
1542 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001543 {
1544 verbose_enter();
1545 smsg((char_u *)
1546 _("Cannot write undo file in any directory in 'undodir'"));
1547 verbose_leave();
1548 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001549 return;
1550 }
1551 }
1552 else
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001553 file_name = name;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001554
1555 /*
1556 * Decide about the permission to use for the undo file. If the buffer
1557 * has a name use the permission of the original file. Otherwise only
1558 * allow the user to access the undo file.
1559 */
1560 perm = 0600;
1561 if (buf->b_ffname != NULL)
1562 {
1563#ifdef UNIX
1564 if (mch_stat((char *)buf->b_ffname, &st_old) >= 0)
1565 {
1566 perm = st_old.st_mode;
1567 st_old_valid = TRUE;
1568 }
1569#else
1570 perm = mch_getperm(buf->b_ffname);
1571 if (perm < 0)
1572 perm = 0600;
1573#endif
1574 }
1575
Bram Moolenaar3cbac302015-04-21 16:12:06 +02001576 /* strip any s-bit and executable bit */
1577 perm = perm & 0666;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001578
1579 /* If the undo file already exists, verify that it actually is an undo
1580 * file, and delete it. */
1581 if (mch_getperm(file_name) >= 0)
1582 {
1583 if (name == NULL || !forceit)
1584 {
1585 /* Check we can read it and it's an undo file. */
1586 fd = mch_open((char *)file_name, O_RDONLY|O_EXTRA, 0);
1587 if (fd < 0)
1588 {
1589 if (name != NULL || p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001590 {
1591 if (name == NULL)
1592 verbose_enter();
1593 smsg((char_u *)
1594 _("Will not overwrite with undo file, cannot read: %s"),
Bram Moolenaar9db58062010-05-29 20:33:07 +02001595 file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001596 if (name == NULL)
1597 verbose_leave();
1598 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001599 goto theend;
1600 }
1601 else
1602 {
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001603 char_u mbuf[UF_START_MAGIC_LEN];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001604 int len;
1605
Bram Moolenaar540fc6f2010-12-17 16:27:16 +01001606 len = read_eintr(fd, mbuf, UF_START_MAGIC_LEN);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001607 close(fd);
1608 if (len < UF_START_MAGIC_LEN
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001609 || memcmp(mbuf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001610 {
1611 if (name != NULL || p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001612 {
1613 if (name == NULL)
1614 verbose_enter();
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001615 smsg((char_u *)
1616 _("Will not overwrite, this is not an undo file: %s"),
Bram Moolenaar9db58062010-05-29 20:33:07 +02001617 file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001618 if (name == NULL)
1619 verbose_leave();
1620 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001621 goto theend;
1622 }
1623 }
1624 }
1625 mch_remove(file_name);
1626 }
1627
Bram Moolenaar504a8212010-05-30 17:17:42 +02001628 /* If there is no undo information at all, quit here after deleting any
1629 * existing undo file. */
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001630 if (buf->b_u_numhead == 0 && buf->b_u_line_ptr == NULL)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001631 {
1632 if (p_verbose > 0)
Bram Moolenaar97ea5112010-06-12 06:46:44 +02001633 verb_msg((char_u *)_("Skipping undo file write, nothing to undo"));
Bram Moolenaar504a8212010-05-30 17:17:42 +02001634 goto theend;
1635 }
1636
Bram Moolenaar9db58062010-05-29 20:33:07 +02001637 fd = mch_open((char *)file_name,
1638 O_CREAT|O_EXTRA|O_WRONLY|O_EXCL|O_NOFOLLOW, perm);
1639 if (fd < 0)
1640 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001641 EMSG2(_(e_not_open), file_name);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001642 goto theend;
1643 }
1644 (void)mch_setperm(file_name, perm);
1645 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001646 {
1647 verbose_enter();
Bram Moolenaar9db58062010-05-29 20:33:07 +02001648 smsg((char_u *)_("Writing undo file: %s"), file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001649 verbose_leave();
1650 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001651
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001652#ifdef U_DEBUG
Bram Moolenaar504a8212010-05-30 17:17:42 +02001653 /* Check there is no problem in undo info before writing. */
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001654 u_check(FALSE);
1655#endif
1656
Bram Moolenaar9db58062010-05-29 20:33:07 +02001657#ifdef UNIX
1658 /*
1659 * Try to set the group of the undo file same as the original file. If
1660 * this fails, set the protection bits for the group same as the
1661 * protection bits for others.
1662 */
Bram Moolenaarce69e822010-07-21 20:31:07 +02001663 if (st_old_valid
1664 && mch_stat((char *)file_name, &st_new) >= 0
1665 && st_new.st_gid != st_old.st_gid
Bram Moolenaar9db58062010-05-29 20:33:07 +02001666# ifdef HAVE_FCHOWN /* sequent-ptx lacks fchown() */
Bram Moolenaarce69e822010-07-21 20:31:07 +02001667 && fchown(fd, (uid_t)-1, st_old.st_gid) != 0
Bram Moolenaar9db58062010-05-29 20:33:07 +02001668# endif
1669 )
1670 mch_setperm(file_name, (perm & 0707) | ((perm & 07) << 3));
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02001671# if defined(HAVE_SELINUX) || defined(HAVE_SMACK)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001672 if (buf->b_ffname != NULL)
1673 mch_copy_sec(buf->b_ffname, file_name);
1674# endif
1675#endif
1676
1677 fp = fdopen(fd, "w");
1678 if (fp == NULL)
1679 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001680 EMSG2(_(e_not_open), file_name);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001681 close(fd);
1682 mch_remove(file_name);
1683 goto theend;
1684 }
1685
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001686 /* Undo must be synced. */
1687 u_sync(TRUE);
1688
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001689 /*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001690 * Write the header. Initializes encryption, if enabled.
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001691 */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001692 bi.bi_buf = buf;
1693 bi.bi_fp = fp;
1694 if (serialize_header(&bi, hash) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001695 goto write_error;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001696
1697 /*
1698 * Iteratively serialize UHPs and their UEPs from the top down.
1699 */
1700 mark = ++lastmark;
1701 uhp = buf->b_u_oldhead;
1702 while (uhp != NULL)
1703 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001704 /* Serialize current UHP if we haven't seen it */
1705 if (uhp->uh_walk != mark)
1706 {
1707 uhp->uh_walk = mark;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001708#ifdef U_DEBUG
1709 ++headers_written;
1710#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001711 if (serialize_uhp(&bi, uhp) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001712 goto write_error;
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001713 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001714
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001715 /* Now walk through the tree - algorithm from undo_time(). */
1716 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != mark)
1717 uhp = uhp->uh_prev.ptr;
1718 else if (uhp->uh_alt_next.ptr != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001719 && uhp->uh_alt_next.ptr->uh_walk != mark)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001720 uhp = uhp->uh_alt_next.ptr;
1721 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001722 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001723 uhp = uhp->uh_next.ptr;
1724 else if (uhp->uh_alt_prev.ptr != NULL)
1725 uhp = uhp->uh_alt_prev.ptr;
1726 else
1727 uhp = uhp->uh_next.ptr;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001728 }
1729
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001730 if (undo_write_bytes(&bi, (long_u)UF_HEADER_END_MAGIC, 2) == OK)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001731 write_ok = TRUE;
1732#ifdef U_DEBUG
1733 if (headers_written != buf->b_u_numhead)
Bram Moolenaar570064c2013-06-10 20:25:10 +02001734 {
1735 EMSGN("Written %ld headers, ...", headers_written);
1736 EMSGN("... but numhead is %ld", buf->b_u_numhead);
1737 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001738#endif
1739
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001740#ifdef FEAT_CRYPT
1741 if (bi.bi_state != NULL && undo_flush(&bi) == FAIL)
1742 write_ok = FALSE;
1743#endif
1744
Bram Moolenaar9db58062010-05-29 20:33:07 +02001745write_error:
1746 fclose(fp);
1747 if (!write_ok)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001748 EMSG2(_("E829: write error in undo file: %s"), file_name);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001749
Bram Moolenaard0573012017-10-28 21:11:06 +02001750#if defined(WIN3264)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001751 /* Copy file attributes; for systems where this can only be done after
1752 * closing the file. */
Bram Moolenaar9db58062010-05-29 20:33:07 +02001753 if (buf->b_ffname != NULL)
1754 (void)mch_copy_file_attribute(buf->b_ffname, file_name);
1755#endif
1756#ifdef HAVE_ACL
1757 if (buf->b_ffname != NULL)
1758 {
1759 vim_acl_T acl;
1760
1761 /* For systems that support ACL: get the ACL from the original file. */
1762 acl = mch_get_acl(buf->b_ffname);
1763 mch_set_acl(file_name, acl);
Bram Moolenaard2aed442012-06-01 13:46:12 +02001764 mch_free_acl(acl);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001765 }
1766#endif
1767
1768theend:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001769#ifdef FEAT_CRYPT
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001770 if (bi.bi_state != NULL)
1771 crypt_free_state(bi.bi_state);
1772 vim_free(bi.bi_buffer);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001773#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02001774 if (file_name != name)
1775 vim_free(file_name);
1776}
1777
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001778/*
1779 * Load the undo tree from an undo file.
1780 * If "name" is not NULL use it as the undo file name. This also means being
1781 * a bit more verbose.
1782 * Otherwise use curbuf->b_ffname to generate the undo file name.
1783 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
1784 */
1785 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001786u_read_undo(char_u *name, char_u *hash, char_u *orig_name)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001787{
1788 char_u *file_name;
1789 FILE *fp;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001790 long version, str_len;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001791 char_u *line_ptr = NULL;
1792 linenr_T line_lnum;
1793 colnr_T line_colnr;
1794 linenr_T line_count;
Bram Moolenaar3eb16372017-02-26 18:11:36 +01001795 long num_head = 0;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001796 long old_header_seq, new_header_seq, cur_header_seq;
1797 long seq_last, seq_cur;
Bram Moolenaarb2c03502010-07-02 20:20:09 +02001798 long last_save_nr = 0;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001799 short old_idx = -1, new_idx = -1, cur_idx = -1;
1800 long num_read_uhps = 0;
1801 time_t seq_time;
1802 int i, j;
1803 int c;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001804 u_header_T *uhp;
1805 u_header_T **uhp_table = NULL;
1806 char_u read_hash[UNDO_HASH_SIZE];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001807 char_u magic_buf[UF_START_MAGIC_LEN];
1808#ifdef U_DEBUG
1809 int *uhp_table_used;
1810#endif
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001811#ifdef UNIX
Bram Moolenaar8767f522016-07-01 17:17:39 +02001812 stat_T st_orig;
1813 stat_T st_undo;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001814#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001815 bufinfo_T bi;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001816
Bram Moolenaar5a669b92014-08-12 20:14:33 +02001817 vim_memset(&bi, 0, sizeof(bi));
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001818 if (name == NULL)
1819 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001820 file_name = u_get_undo_file_name(curbuf->b_ffname, TRUE);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001821 if (file_name == NULL)
1822 return;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001823
1824#ifdef UNIX
1825 /* For safety we only read an undo file if the owner is equal to the
Bram Moolenaar3b262392013-09-08 15:40:49 +02001826 * owner of the text file or equal to the current user. */
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001827 if (mch_stat((char *)orig_name, &st_orig) >= 0
1828 && mch_stat((char *)file_name, &st_undo) >= 0
Bram Moolenaar3b262392013-09-08 15:40:49 +02001829 && st_orig.st_uid != st_undo.st_uid
1830 && st_undo.st_uid != getuid())
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001831 {
1832 if (p_verbose > 0)
1833 {
1834 verbose_enter();
1835 smsg((char_u *)_("Not reading undo file, owner differs: %s"),
1836 file_name);
1837 verbose_leave();
1838 }
1839 return;
1840 }
1841#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001842 }
1843 else
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001844 file_name = name;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001845
1846 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001847 {
1848 verbose_enter();
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001849 smsg((char_u *)_("Reading undo file: %s"), file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001850 verbose_leave();
1851 }
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001852
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001853 fp = mch_fopen((char *)file_name, "r");
1854 if (fp == NULL)
1855 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001856 if (name != NULL || p_verbose > 0)
1857 EMSG2(_("E822: Cannot open undo file for reading: %s"), file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001858 goto error;
1859 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001860 bi.bi_buf = curbuf;
1861 bi.bi_fp = fp;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001862
Bram Moolenaar9db58062010-05-29 20:33:07 +02001863 /*
1864 * Read the undo file header.
1865 */
1866 if (fread(magic_buf, UF_START_MAGIC_LEN, 1, fp) != 1
1867 || memcmp(magic_buf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001868 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001869 EMSG2(_("E823: Not an undo file: %s"), file_name);
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001870 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001871 }
1872 version = get2c(fp);
Bram Moolenaar69154f22010-07-18 21:42:34 +02001873 if (version == UF_VERSION_CRYPT)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001874 {
1875#ifdef FEAT_CRYPT
Bram Moolenaar56be9502010-06-06 14:20:26 +02001876 if (*curbuf->b_p_key == NUL)
1877 {
1878 EMSG2(_("E832: Non-encrypted file has encrypted undo file: %s"),
1879 file_name);
1880 goto error;
1881 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001882 bi.bi_state = crypt_create_from_file(fp, curbuf->b_p_key);
1883 if (bi.bi_state == NULL)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001884 {
1885 EMSG2(_("E826: Undo file decryption failed: %s"), file_name);
1886 goto error;
1887 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001888 if (crypt_whole_undofile(bi.bi_state->method_nr))
1889 {
1890 bi.bi_buffer = alloc(CRYPT_BUF_SIZE);
1891 if (bi.bi_buffer == NULL)
1892 {
1893 crypt_free_state(bi.bi_state);
1894 bi.bi_state = NULL;
1895 goto error;
1896 }
1897 bi.bi_avail = 0;
1898 bi.bi_used = 0;
1899 }
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001900#else
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001901 EMSG2(_("E827: Undo file is encrypted: %s"), file_name);
1902 goto error;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001903#endif
1904 }
Bram Moolenaar69154f22010-07-18 21:42:34 +02001905 else if (version != UF_VERSION)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001906 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001907 EMSG2(_("E824: Incompatible undo file: %s"), file_name);
1908 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001909 }
1910
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001911 if (undo_read(&bi, read_hash, (size_t)UNDO_HASH_SIZE) == FAIL)
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001912 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001913 corruption_error("hash", file_name);
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001914 goto error;
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001915 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001916 line_count = (linenr_T)undo_read_4c(&bi);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001917 if (memcmp(hash, read_hash, UNDO_HASH_SIZE) != 0
1918 || line_count != curbuf->b_ml.ml_line_count)
1919 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001920 if (p_verbose > 0 || name != NULL)
1921 {
Bram Moolenaar504a8212010-05-30 17:17:42 +02001922 if (name == NULL)
1923 verbose_enter();
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001924 give_warning((char_u *)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001925 _("File contents changed, cannot use undo info"), TRUE);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001926 if (name == NULL)
1927 verbose_leave();
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001928 }
1929 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001930 }
1931
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001932 /* Read undo data for "U" command. */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001933 str_len = undo_read_4c(&bi);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001934 if (str_len < 0)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001935 goto error;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001936 if (str_len > 0)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001937 line_ptr = read_string_decrypt(&bi, str_len);
1938 line_lnum = (linenr_T)undo_read_4c(&bi);
1939 line_colnr = (colnr_T)undo_read_4c(&bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001940 if (line_lnum < 0 || line_colnr < 0)
1941 {
1942 corruption_error("line lnum/col", file_name);
1943 goto error;
1944 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001945
1946 /* Begin general undo data */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001947 old_header_seq = undo_read_4c(&bi);
1948 new_header_seq = undo_read_4c(&bi);
1949 cur_header_seq = undo_read_4c(&bi);
1950 num_head = undo_read_4c(&bi);
1951 seq_last = undo_read_4c(&bi);
1952 seq_cur = undo_read_4c(&bi);
1953 seq_time = undo_read_time(&bi);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001954
Bram Moolenaar69154f22010-07-18 21:42:34 +02001955 /* Optional header fields. */
1956 for (;;)
1957 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001958 int len = undo_read_byte(&bi);
Bram Moolenaar69154f22010-07-18 21:42:34 +02001959 int what;
Bram Moolenaara800b422010-06-27 01:15:55 +02001960
Bram Moolenaar69154f22010-07-18 21:42:34 +02001961 if (len == 0 || len == EOF)
1962 break;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001963 what = undo_read_byte(&bi);
Bram Moolenaar69154f22010-07-18 21:42:34 +02001964 switch (what)
1965 {
1966 case UF_LAST_SAVE_NR:
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001967 last_save_nr = undo_read_4c(&bi);
Bram Moolenaara800b422010-06-27 01:15:55 +02001968 break;
Bram Moolenaar69154f22010-07-18 21:42:34 +02001969 default:
1970 /* field not supported, skip */
1971 while (--len >= 0)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001972 (void)undo_read_byte(&bi);
Bram Moolenaara800b422010-06-27 01:15:55 +02001973 }
Bram Moolenaar69154f22010-07-18 21:42:34 +02001974 }
Bram Moolenaara800b422010-06-27 01:15:55 +02001975
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001976 /* uhp_table will store the freshly created undo headers we allocate
1977 * until we insert them into curbuf. The table remains sorted by the
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001978 * sequence numbers of the headers.
1979 * When there are no headers uhp_table is NULL. */
1980 if (num_head > 0)
1981 {
Bram Moolenaar3eb16372017-02-26 18:11:36 +01001982 if (num_head < LONG_MAX / (long)sizeof(u_header_T *))
1983 uhp_table = (u_header_T **)U_ALLOC_LINE(
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001984 num_head * sizeof(u_header_T *));
1985 if (uhp_table == NULL)
1986 goto error;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02001987 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001988
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001989 while ((c = undo_read_2c(&bi)) == UF_HEADER_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001990 {
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001991 if (num_read_uhps >= num_head)
1992 {
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001993 corruption_error("num_head too small", file_name);
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02001994 goto error;
1995 }
1996
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001997 uhp = unserialize_uhp(&bi, file_name);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001998 if (uhp == NULL)
1999 goto error;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02002000 uhp_table[num_read_uhps++] = uhp;
2001 }
2002
2003 if (num_read_uhps != num_head)
2004 {
2005 corruption_error("num_head", file_name);
2006 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002007 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02002008 if (c != UF_HEADER_END_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002009 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02002010 corruption_error("end marker", file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002011 goto error;
2012 }
2013
Bram Moolenaar9db58062010-05-29 20:33:07 +02002014#ifdef U_DEBUG
2015 uhp_table_used = (int *)alloc_clear(
2016 (unsigned)(sizeof(int) * num_head + 1));
2017# define SET_FLAG(j) ++uhp_table_used[j]
2018#else
2019# define SET_FLAG(j)
2020#endif
2021
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02002022 /* We have put all of the headers into a table. Now we iterate through the
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002023 * table and swizzle each sequence number we have stored in uh_*_seq into
2024 * a pointer corresponding to the header with that sequence number. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002025 for (i = 0; i < num_head; i++)
2026 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002027 uhp = uhp_table[i];
2028 if (uhp == NULL)
2029 continue;
2030 for (j = 0; j < num_head; j++)
2031 if (uhp_table[j] != NULL && i != j
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002032 && uhp_table[i]->uh_seq == uhp_table[j]->uh_seq)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002033 {
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02002034 corruption_error("duplicate uh_seq", file_name);
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002035 goto error;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02002036 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002037 for (j = 0; j < num_head; j++)
2038 if (uhp_table[j] != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002039 && uhp_table[j]->uh_seq == uhp->uh_next.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002040 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002041 uhp->uh_next.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02002042 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002043 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002044 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002045 for (j = 0; j < num_head; j++)
2046 if (uhp_table[j] != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002047 && uhp_table[j]->uh_seq == uhp->uh_prev.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002048 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002049 uhp->uh_prev.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02002050 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002051 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002052 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002053 for (j = 0; j < num_head; j++)
2054 if (uhp_table[j] != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002055 && uhp_table[j]->uh_seq == uhp->uh_alt_next.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002056 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002057 uhp->uh_alt_next.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02002058 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002059 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002060 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002061 for (j = 0; j < num_head; j++)
2062 if (uhp_table[j] != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002063 && uhp_table[j]->uh_seq == uhp->uh_alt_prev.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002064 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002065 uhp->uh_alt_prev.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02002066 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002067 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002068 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002069 if (old_header_seq > 0 && old_idx < 0 && uhp->uh_seq == old_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002070 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002071 old_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002072 SET_FLAG(i);
2073 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002074 if (new_header_seq > 0 && new_idx < 0 && uhp->uh_seq == new_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002075 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002076 new_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002077 SET_FLAG(i);
2078 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002079 if (cur_header_seq > 0 && cur_idx < 0 && uhp->uh_seq == cur_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002080 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002081 cur_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002082 SET_FLAG(i);
2083 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002084 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02002085
2086 /* Now that we have read the undo info successfully, free the current undo
2087 * info and use the info from the file. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002088 u_blockfree(curbuf);
Bram Moolenaar9db58062010-05-29 20:33:07 +02002089 curbuf->b_u_oldhead = old_idx < 0 ? NULL : uhp_table[old_idx];
2090 curbuf->b_u_newhead = new_idx < 0 ? NULL : uhp_table[new_idx];
2091 curbuf->b_u_curhead = cur_idx < 0 ? NULL : uhp_table[cur_idx];
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002092 curbuf->b_u_line_ptr = line_ptr;
2093 curbuf->b_u_line_lnum = line_lnum;
2094 curbuf->b_u_line_colnr = line_colnr;
2095 curbuf->b_u_numhead = num_head;
2096 curbuf->b_u_seq_last = seq_last;
2097 curbuf->b_u_seq_cur = seq_cur;
Bram Moolenaara800b422010-06-27 01:15:55 +02002098 curbuf->b_u_time_cur = seq_time;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002099 curbuf->b_u_save_nr_last = last_save_nr;
Bram Moolenaardba01a02010-11-03 19:32:42 +01002100 curbuf->b_u_save_nr_cur = last_save_nr;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02002101
2102 curbuf->b_u_synced = TRUE;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002103 vim_free(uhp_table);
Bram Moolenaar9db58062010-05-29 20:33:07 +02002104
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002105#ifdef U_DEBUG
Bram Moolenaar9db58062010-05-29 20:33:07 +02002106 for (i = 0; i < num_head; ++i)
2107 if (uhp_table_used[i] == 0)
2108 EMSGN("uhp_table entry %ld not used, leaking memory", i);
2109 vim_free(uhp_table_used);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002110 u_check(TRUE);
2111#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02002112
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002113 if (name != NULL)
2114 smsg((char_u *)_("Finished reading undo file %s"), file_name);
2115 goto theend;
2116
2117error:
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002118 vim_free(line_ptr);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002119 if (uhp_table != NULL)
2120 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002121 for (i = 0; i < num_read_uhps; i++)
2122 if (uhp_table[i] != NULL)
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02002123 u_free_uhp(uhp_table[i]);
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002124 vim_free(uhp_table);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002125 }
2126
2127theend:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002128#ifdef FEAT_CRYPT
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002129 if (bi.bi_state != NULL)
2130 crypt_free_state(bi.bi_state);
2131 vim_free(bi.bi_buffer);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002132#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002133 if (fp != NULL)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002134 fclose(fp);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002135 if (file_name != name)
2136 vim_free(file_name);
2137 return;
2138}
2139
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002140#endif /* FEAT_PERSISTENT_UNDO */
2141
2142
Bram Moolenaar071d4272004-06-13 20:20:40 +00002143/*
2144 * If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible).
2145 * If 'cpoptions' does not contain 'u': Always undo.
2146 */
2147 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002148u_undo(int count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002149{
2150 /*
2151 * If we get an undo command while executing a macro, we behave like the
2152 * original vi. If this happens twice in one macro the result will not
2153 * be compatible.
2154 */
2155 if (curbuf->b_u_synced == FALSE)
2156 {
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002157 u_sync(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002158 count = 1;
2159 }
2160
2161 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
2162 undo_undoes = TRUE;
2163 else
2164 undo_undoes = !undo_undoes;
2165 u_doit(count);
2166}
2167
2168/*
2169 * If 'cpoptions' contains 'u': Repeat the previous undo or redo.
2170 * If 'cpoptions' does not contain 'u': Always redo.
2171 */
2172 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002173u_redo(int count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002174{
2175 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
2176 undo_undoes = FALSE;
2177 u_doit(count);
2178}
2179
2180/*
2181 * Undo or redo, depending on 'undo_undoes', 'count' times.
2182 */
2183 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002184u_doit(int startcount)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002185{
Bram Moolenaarca003e12006-03-17 23:19:38 +00002186 int count = startcount;
2187
Bram Moolenaar8ada17c2006-01-19 22:16:24 +00002188 if (!undo_allowed())
Bram Moolenaar071d4272004-06-13 20:20:40 +00002189 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002190
2191 u_newcount = 0;
2192 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00002193 if (curbuf->b_ml.ml_flags & ML_EMPTY)
2194 u_oldcount = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002195 while (count--)
2196 {
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002197 /* Do the change warning now, so that it triggers FileChangedRO when
2198 * needed. This may cause the file to be reloaded, that must happen
2199 * before we do anything, because it may change curbuf->b_u_curhead
2200 * and more. */
2201 change_warning(0);
2202
Bram Moolenaar071d4272004-06-13 20:20:40 +00002203 if (undo_undoes)
2204 {
2205 if (curbuf->b_u_curhead == NULL) /* first undo */
2206 curbuf->b_u_curhead = curbuf->b_u_newhead;
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +01002207 else if (get_undolevel() > 0) /* multi level undo */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002208 /* get next undo */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002209 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002210 /* nothing to undo */
2211 if (curbuf->b_u_numhead == 0 || curbuf->b_u_curhead == NULL)
2212 {
2213 /* stick curbuf->b_u_curhead at end */
2214 curbuf->b_u_curhead = curbuf->b_u_oldhead;
2215 beep_flush();
Bram Moolenaarca003e12006-03-17 23:19:38 +00002216 if (count == startcount - 1)
2217 {
2218 MSG(_("Already at oldest change"));
2219 return;
2220 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002221 break;
2222 }
2223
Bram Moolenaarca003e12006-03-17 23:19:38 +00002224 u_undoredo(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002225 }
2226 else
2227 {
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +01002228 if (curbuf->b_u_curhead == NULL || get_undolevel() <= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002229 {
2230 beep_flush(); /* nothing to redo */
Bram Moolenaarca003e12006-03-17 23:19:38 +00002231 if (count == startcount - 1)
2232 {
2233 MSG(_("Already at newest change"));
2234 return;
2235 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002236 break;
2237 }
2238
Bram Moolenaarca003e12006-03-17 23:19:38 +00002239 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002240
2241 /* Advance for next redo. Set "newhead" when at the end of the
2242 * redoable changes. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002243 if (curbuf->b_u_curhead->uh_prev.ptr == NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002244 curbuf->b_u_newhead = curbuf->b_u_curhead;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002245 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002246 }
2247 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00002248 u_undo_end(undo_undoes, FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002249}
2250
Bram Moolenaar1e607892006-03-13 22:15:53 +00002251/*
2252 * Undo or redo over the timeline.
2253 * When "step" is negative go back in time, otherwise goes forward in time.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002254 * When "sec" is FALSE make "step" steps, when "sec" is TRUE use "step" as
2255 * seconds.
Bram Moolenaar730cde92010-06-27 05:18:54 +02002256 * When "file" is TRUE use "step" as a number of file writes.
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002257 * When "absolute" is TRUE use "step" as the sequence number to jump to.
2258 * "sec" must be FALSE then.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002259 */
2260 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002261undo_time(
2262 long step,
2263 int sec,
2264 int file,
2265 int absolute)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002266{
2267 long target;
2268 long closest;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002269 long closest_start;
2270 long closest_seq = 0;
2271 long val;
Bram Moolenaarce46d932018-01-30 22:46:06 +01002272 u_header_T *uhp = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002273 u_header_T *last;
2274 int mark;
2275 int nomark;
2276 int round;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002277 int dosec = sec;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002278 int dofile = file;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002279 int above = FALSE;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002280 int did_undo = TRUE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002281
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00002282 /* First make sure the current undoable change is synced. */
2283 if (curbuf->b_u_synced == FALSE)
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002284 u_sync(TRUE);
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00002285
Bram Moolenaar1e607892006-03-13 22:15:53 +00002286 u_newcount = 0;
2287 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00002288 if (curbuf->b_ml.ml_flags & ML_EMPTY)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002289 u_oldcount = -1;
2290
Bram Moolenaarca003e12006-03-17 23:19:38 +00002291 /* "target" is the node below which we want to be.
2292 * Init "closest" to a value we can't reach. */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002293 if (absolute)
2294 {
Bram Moolenaarce46d932018-01-30 22:46:06 +01002295 target = step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002296 closest = -1;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002297 }
Bram Moolenaarca003e12006-03-17 23:19:38 +00002298 else
Bram Moolenaar1e607892006-03-13 22:15:53 +00002299 {
Bram Moolenaar730cde92010-06-27 05:18:54 +02002300 if (dosec)
Bram Moolenaarcbd4de42017-01-07 16:14:57 +01002301 target = (long)(curbuf->b_u_time_cur) + step;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002302 else if (dofile)
2303 {
2304 if (step < 0)
2305 {
2306 /* Going back to a previous write. If there were changes after
2307 * the last write, count that as moving one file-write, so
2308 * that ":earlier 1f" undoes all changes since the last save. */
2309 uhp = curbuf->b_u_curhead;
2310 if (uhp != NULL)
2311 uhp = uhp->uh_next.ptr;
2312 else
2313 uhp = curbuf->b_u_newhead;
2314 if (uhp != NULL && uhp->uh_save_nr != 0)
2315 /* "uh_save_nr" was set in the last block, that means
2316 * there were no changes since the last write */
2317 target = curbuf->b_u_save_nr_cur + step;
2318 else
2319 /* count the changes since the last write as one step */
2320 target = curbuf->b_u_save_nr_cur + step + 1;
2321 if (target <= 0)
2322 /* Go to before first write: before the oldest change. Use
2323 * the sequence number for that. */
2324 dofile = FALSE;
2325 }
2326 else
2327 {
2328 /* Moving forward to a newer write. */
2329 target = curbuf->b_u_save_nr_cur + step;
2330 if (target > curbuf->b_u_save_nr_last)
2331 {
2332 /* Go to after last write: after the latest change. Use
2333 * the sequence number for that. */
2334 target = curbuf->b_u_seq_last + 1;
2335 dofile = FALSE;
2336 }
2337 }
2338 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002339 else
2340 target = curbuf->b_u_seq_cur + step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002341 if (step < 0)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002342 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002343 if (target < 0)
2344 target = 0;
2345 closest = -1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002346 }
2347 else
2348 {
Bram Moolenaar730cde92010-06-27 05:18:54 +02002349 if (dosec)
Bram Moolenaarcbd4de42017-01-07 16:14:57 +01002350 closest = (long)(vim_time() + 1);
Bram Moolenaar730cde92010-06-27 05:18:54 +02002351 else if (dofile)
2352 closest = curbuf->b_u_save_nr_last + 2;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002353 else
2354 closest = curbuf->b_u_seq_last + 2;
2355 if (target >= closest)
2356 target = closest - 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002357 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002358 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002359 closest_start = closest;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002360 closest_seq = curbuf->b_u_seq_cur;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002361
Bram Moolenaarce46d932018-01-30 22:46:06 +01002362 /* When "target" is 0; Back to origin. */
2363 if (target == 0)
Bram Moolenaar059fd012018-01-31 14:25:53 +01002364 {
2365 mark = lastmark; /* avoid that GCC complains */
2366 goto target_zero;
2367 }
Bram Moolenaarce46d932018-01-30 22:46:06 +01002368
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002369 /*
2370 * May do this twice:
Bram Moolenaar1e607892006-03-13 22:15:53 +00002371 * 1. Search for "target", update "closest" to the best match found.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002372 * 2. If "target" not found search for "closest".
2373 *
2374 * When using the closest time we use the sequence number in the second
2375 * round, because there may be several entries with the same time.
2376 */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002377 for (round = 1; round <= 2; ++round)
2378 {
2379 /* Find the path from the current state to where we want to go. The
2380 * desired state can be anywhere in the undo tree, need to go all over
2381 * it. We put "nomark" in uh_walk where we have been without success,
2382 * "mark" where it could possibly be. */
2383 mark = ++lastmark;
2384 nomark = ++lastmark;
2385
2386 if (curbuf->b_u_curhead == NULL) /* at leaf of the tree */
2387 uhp = curbuf->b_u_newhead;
2388 else
2389 uhp = curbuf->b_u_curhead;
2390
2391 while (uhp != NULL)
2392 {
2393 uhp->uh_walk = mark;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002394 if (dosec)
Bram Moolenaarcbd4de42017-01-07 16:14:57 +01002395 val = (long)(uhp->uh_time);
Bram Moolenaar730cde92010-06-27 05:18:54 +02002396 else if (dofile)
2397 val = uhp->uh_save_nr;
2398 else
2399 val = uhp->uh_seq;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002400
Bram Moolenaar730cde92010-06-27 05:18:54 +02002401 if (round == 1 && !(dofile && val == 0))
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002402 {
2403 /* Remember the header that is closest to the target.
2404 * It must be at least in the right direction (checked with
Bram Moolenaarca003e12006-03-17 23:19:38 +00002405 * "b_u_seq_cur"). When the timestamp is equal find the
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002406 * highest/lowest sequence number. */
Bram Moolenaarca003e12006-03-17 23:19:38 +00002407 if ((step < 0 ? uhp->uh_seq <= curbuf->b_u_seq_cur
2408 : uhp->uh_seq > curbuf->b_u_seq_cur)
2409 && ((dosec && val == closest)
2410 ? (step < 0
2411 ? uhp->uh_seq < closest_seq
2412 : uhp->uh_seq > closest_seq)
2413 : closest == closest_start
2414 || (val > target
2415 ? (closest > target
2416 ? val - target <= closest - target
2417 : val - target <= target - closest)
2418 : (closest > target
2419 ? target - val <= closest - target
2420 : target - val <= target - closest))))
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002421 {
2422 closest = val;
2423 closest_seq = uhp->uh_seq;
2424 }
2425 }
2426
2427 /* Quit searching when we found a match. But when searching for a
2428 * time we need to continue looking for the best uh_seq. */
2429 if (target == val && !dosec)
Bram Moolenaar730cde92010-06-27 05:18:54 +02002430 {
2431 target = uhp->uh_seq;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002432 break;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002433 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002434
2435 /* go down in the tree if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002436 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
2437 && uhp->uh_prev.ptr->uh_walk != mark)
2438 uhp = uhp->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002439
2440 /* go to alternate branch if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002441 else if (uhp->uh_alt_next.ptr != NULL
2442 && uhp->uh_alt_next.ptr->uh_walk != nomark
2443 && uhp->uh_alt_next.ptr->uh_walk != mark)
2444 uhp = uhp->uh_alt_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002445
2446 /* go up in the tree if we haven't been there and we are at the
2447 * start of alternate branches */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002448 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
2449 && uhp->uh_next.ptr->uh_walk != nomark
2450 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002451 {
2452 /* If still at the start we don't go through this change. */
2453 if (uhp == curbuf->b_u_curhead)
2454 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002455 uhp = uhp->uh_next.ptr;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002456 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002457
2458 else
2459 {
2460 /* need to backtrack; mark this node as useless */
2461 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002462 if (uhp->uh_alt_prev.ptr != NULL)
2463 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002464 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002465 uhp = uhp->uh_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002466 }
2467 }
2468
2469 if (uhp != NULL) /* found it */
2470 break;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002471
2472 if (absolute)
2473 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002474 EMSGN(_("E830: Undo number %ld not found"), step);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002475 return;
2476 }
2477
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002478 if (closest == closest_start)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002479 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002480 if (step < 0)
2481 MSG(_("Already at oldest change"));
2482 else
2483 MSG(_("Already at newest change"));
Bram Moolenaar1e607892006-03-13 22:15:53 +00002484 return;
2485 }
2486
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002487 target = closest_seq;
2488 dosec = FALSE;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002489 dofile = FALSE;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002490 if (step < 0)
2491 above = TRUE; /* stop above the header */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002492 }
2493
Bram Moolenaar059fd012018-01-31 14:25:53 +01002494target_zero:
Bram Moolenaar1e607892006-03-13 22:15:53 +00002495 /* If we found it: Follow the path to go to where we want to be. */
Bram Moolenaarce46d932018-01-30 22:46:06 +01002496 if (uhp != NULL || target == 0)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002497 {
2498 /*
2499 * First go up the tree as much as needed.
2500 */
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002501 while (!got_int)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002502 {
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002503 /* Do the change warning now, for the same reason as above. */
2504 change_warning(0);
2505
Bram Moolenaar1e607892006-03-13 22:15:53 +00002506 uhp = curbuf->b_u_curhead;
2507 if (uhp == NULL)
2508 uhp = curbuf->b_u_newhead;
2509 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002510 uhp = uhp->uh_next.ptr;
Bram Moolenaarce46d932018-01-30 22:46:06 +01002511 if (uhp == NULL || (target > 0 && uhp->uh_walk != mark)
Bram Moolenaarca003e12006-03-17 23:19:38 +00002512 || (uhp->uh_seq == target && !above))
Bram Moolenaar1e607892006-03-13 22:15:53 +00002513 break;
2514 curbuf->b_u_curhead = uhp;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002515 u_undoredo(TRUE);
Bram Moolenaarce46d932018-01-30 22:46:06 +01002516 if (target > 0)
2517 uhp->uh_walk = nomark; /* don't go back down here */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002518 }
2519
Bram Moolenaarce46d932018-01-30 22:46:06 +01002520 /* When back to origin, redo is not needed. */
2521 if (target > 0)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002522 {
Bram Moolenaarce46d932018-01-30 22:46:06 +01002523 /*
2524 * And now go down the tree (redo), branching off where needed.
2525 */
2526 while (!got_int)
2527 {
2528 /* Do the change warning now, for the same reason as above. */
2529 change_warning(0);
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002530
Bram Moolenaarce46d932018-01-30 22:46:06 +01002531 uhp = curbuf->b_u_curhead;
2532 if (uhp == NULL)
2533 break;
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002534
Bram Moolenaarce46d932018-01-30 22:46:06 +01002535 /* Go back to the first branch with a mark. */
2536 while (uhp->uh_alt_prev.ptr != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002537 && uhp->uh_alt_prev.ptr->uh_walk == mark)
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002538 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaarce46d932018-01-30 22:46:06 +01002539
2540 /* Find the last branch with a mark, that's the one. */
2541 last = uhp;
2542 while (last->uh_alt_next.ptr != NULL
2543 && last->uh_alt_next.ptr->uh_walk == mark)
2544 last = last->uh_alt_next.ptr;
2545 if (last != uhp)
2546 {
2547 /* Make the used branch the first entry in the list of
2548 * alternatives to make "u" and CTRL-R take this branch. */
2549 while (uhp->uh_alt_prev.ptr != NULL)
2550 uhp = uhp->uh_alt_prev.ptr;
2551 if (last->uh_alt_next.ptr != NULL)
2552 last->uh_alt_next.ptr->uh_alt_prev.ptr =
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002553 last->uh_alt_prev.ptr;
Bram Moolenaarce46d932018-01-30 22:46:06 +01002554 last->uh_alt_prev.ptr->uh_alt_next.ptr =
2555 last->uh_alt_next.ptr;
2556 last->uh_alt_prev.ptr = NULL;
2557 last->uh_alt_next.ptr = uhp;
2558 uhp->uh_alt_prev.ptr = last;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002559
Bram Moolenaarce46d932018-01-30 22:46:06 +01002560 if (curbuf->b_u_oldhead == uhp)
2561 curbuf->b_u_oldhead = last;
2562 uhp = last;
2563 if (uhp->uh_next.ptr != NULL)
2564 uhp->uh_next.ptr->uh_prev.ptr = uhp;
2565 }
2566 curbuf->b_u_curhead = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002567
Bram Moolenaarce46d932018-01-30 22:46:06 +01002568 if (uhp->uh_walk != mark)
2569 break; /* must have reached the target */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002570
Bram Moolenaarce46d932018-01-30 22:46:06 +01002571 /* Stop when going backwards in time and didn't find the exact
2572 * header we were looking for. */
2573 if (uhp->uh_seq == target && above)
2574 {
2575 curbuf->b_u_seq_cur = target - 1;
2576 break;
2577 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002578
Bram Moolenaarce46d932018-01-30 22:46:06 +01002579 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002580
Bram Moolenaarce46d932018-01-30 22:46:06 +01002581 /* Advance "curhead" to below the header we last used. If it
2582 * becomes NULL then we need to set "newhead" to this leaf. */
2583 if (uhp->uh_prev.ptr == NULL)
2584 curbuf->b_u_newhead = uhp;
2585 curbuf->b_u_curhead = uhp->uh_prev.ptr;
2586 did_undo = FALSE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002587
Bram Moolenaarce46d932018-01-30 22:46:06 +01002588 if (uhp->uh_seq == target) /* found it! */
2589 break;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002590
Bram Moolenaarce46d932018-01-30 22:46:06 +01002591 uhp = uhp->uh_prev.ptr;
2592 if (uhp == NULL || uhp->uh_walk != mark)
2593 {
2594 /* Need to redo more but can't find it... */
2595 internal_error("undo_time()");
2596 break;
2597 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002598 }
2599 }
2600 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00002601 u_undo_end(did_undo, absolute);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002602}
2603
2604/*
2605 * u_undoredo: common code for undo and redo
2606 *
2607 * The lines in the file are replaced by the lines in the entry list at
2608 * curbuf->b_u_curhead. The replaced lines in the file are saved in the entry
2609 * list for the next undo/redo.
Bram Moolenaarca003e12006-03-17 23:19:38 +00002610 *
2611 * When "undo" is TRUE we go up in the tree, when FALSE we go down.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002612 */
2613 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002614u_undoredo(int undo)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002615{
2616 char_u **newarray = NULL;
2617 linenr_T oldsize;
2618 linenr_T newsize;
2619 linenr_T top, bot;
2620 linenr_T lnum;
2621 linenr_T newlnum = MAXLNUM;
2622 long i;
2623 u_entry_T *uep, *nuep;
2624 u_entry_T *newlist = NULL;
2625 int old_flags;
2626 int new_flags;
2627 pos_T namedm[NMARKS];
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002628 visualinfo_T visualinfo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002629 int empty_buffer; /* buffer became empty */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002630 u_header_T *curhead = curbuf->b_u_curhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002631
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002632 /* Don't want autocommands using the undo structures here, they are
2633 * invalid till the end. */
2634 block_autocmds();
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002635
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002636#ifdef U_DEBUG
2637 u_check(FALSE);
2638#endif
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002639 old_flags = curhead->uh_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002640 new_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
2641 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
2642 setpcmark();
2643
2644 /*
2645 * save marks before undo/redo
2646 */
2647 mch_memmove(namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002648 visualinfo = curbuf->b_visual;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002649 curbuf->b_op_start.lnum = curbuf->b_ml.ml_line_count;
2650 curbuf->b_op_start.col = 0;
2651 curbuf->b_op_end.lnum = 0;
2652 curbuf->b_op_end.col = 0;
2653
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002654 for (uep = curhead->uh_entry; uep != NULL; uep = nuep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002655 {
2656 top = uep->ue_top;
2657 bot = uep->ue_bot;
2658 if (bot == 0)
2659 bot = curbuf->b_ml.ml_line_count + 1;
2660 if (top > curbuf->b_ml.ml_line_count || top >= bot
2661 || bot > curbuf->b_ml.ml_line_count + 1)
2662 {
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002663 unblock_autocmds();
Bram Moolenaar95f09602016-11-10 20:01:45 +01002664 IEMSG(_("E438: u_undo: line numbers wrong"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002665 changed(); /* don't want UNCHANGED now */
2666 return;
2667 }
2668
2669 oldsize = bot - top - 1; /* number of lines before undo */
2670 newsize = uep->ue_size; /* number of lines after undo */
2671
2672 if (top < newlnum)
2673 {
2674 /* If the saved cursor is somewhere in this undo block, move it to
2675 * the remembered position. Makes "gwap" put the cursor back
2676 * where it was. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002677 lnum = curhead->uh_cursor.lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002678 if (lnum >= top && lnum <= top + newsize + 1)
2679 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002680 curwin->w_cursor = curhead->uh_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002681 newlnum = curwin->w_cursor.lnum - 1;
2682 }
2683 else
2684 {
2685 /* Use the first line that actually changed. Avoids that
2686 * undoing auto-formatting puts the cursor in the previous
2687 * line. */
2688 for (i = 0; i < newsize && i < oldsize; ++i)
2689 if (STRCMP(uep->ue_array[i], ml_get(top + 1 + i)) != 0)
2690 break;
2691 if (i == newsize && newlnum == MAXLNUM && uep->ue_next == NULL)
2692 {
2693 newlnum = top;
2694 curwin->w_cursor.lnum = newlnum + 1;
2695 }
2696 else if (i < newsize)
2697 {
2698 newlnum = top + i;
2699 curwin->w_cursor.lnum = newlnum + 1;
2700 }
2701 }
2702 }
2703
2704 empty_buffer = FALSE;
2705
2706 /* delete the lines between top and bot and save them in newarray */
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002707 if (oldsize > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002708 {
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002709 if ((newarray = (char_u **)U_ALLOC_LINE(
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002710 sizeof(char_u *) * oldsize)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002711 {
2712 do_outofmem_msg((long_u)(sizeof(char_u *) * oldsize));
2713 /*
2714 * We have messed up the entry list, repair is impossible.
2715 * we have to free the rest of the list.
2716 */
2717 while (uep != NULL)
2718 {
2719 nuep = uep->ue_next;
2720 u_freeentry(uep, uep->ue_size);
2721 uep = nuep;
2722 }
2723 break;
2724 }
2725 /* delete backwards, it goes faster in most cases */
2726 for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum)
2727 {
2728 /* what can we do when we run out of memory? */
2729 if ((newarray[i] = u_save_line(lnum)) == NULL)
2730 do_outofmem_msg((long_u)0);
2731 /* remember we deleted the last line in the buffer, and a
2732 * dummy empty line will be inserted */
2733 if (curbuf->b_ml.ml_line_count == 1)
2734 empty_buffer = TRUE;
2735 ml_delete(lnum, FALSE);
2736 }
2737 }
Bram Moolenaar8d343302005-07-12 22:46:17 +00002738 else
2739 newarray = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002740
2741 /* insert the lines in u_array between top and bot */
2742 if (newsize)
2743 {
2744 for (lnum = top, i = 0; i < newsize; ++i, ++lnum)
2745 {
2746 /*
2747 * If the file is empty, there is an empty line 1 that we
2748 * should get rid of, by replacing it with the new line
2749 */
2750 if (empty_buffer && lnum == 0)
2751 ml_replace((linenr_T)1, uep->ue_array[i], TRUE);
2752 else
2753 ml_append(lnum, uep->ue_array[i], (colnr_T)0, FALSE);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002754 vim_free(uep->ue_array[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002755 }
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002756 vim_free((char_u *)uep->ue_array);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002757 }
2758
2759 /* adjust marks */
2760 if (oldsize != newsize)
2761 {
2762 mark_adjust(top + 1, top + oldsize, (long)MAXLNUM,
2763 (long)newsize - (long)oldsize);
2764 if (curbuf->b_op_start.lnum > top + oldsize)
2765 curbuf->b_op_start.lnum += newsize - oldsize;
2766 if (curbuf->b_op_end.lnum > top + oldsize)
2767 curbuf->b_op_end.lnum += newsize - oldsize;
2768 }
2769
2770 changed_lines(top + 1, 0, bot, newsize - oldsize);
2771
2772 /* set '[ and '] mark */
2773 if (top + 1 < curbuf->b_op_start.lnum)
2774 curbuf->b_op_start.lnum = top + 1;
2775 if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum)
2776 curbuf->b_op_end.lnum = top + 1;
2777 else if (top + newsize > curbuf->b_op_end.lnum)
2778 curbuf->b_op_end.lnum = top + newsize;
2779
2780 u_newcount += newsize;
2781 u_oldcount += oldsize;
2782 uep->ue_size = oldsize;
2783 uep->ue_array = newarray;
2784 uep->ue_bot = top + newsize + 1;
2785
2786 /*
2787 * insert this entry in front of the new entry list
2788 */
2789 nuep = uep->ue_next;
2790 uep->ue_next = newlist;
2791 newlist = uep;
2792 }
2793
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002794 curhead->uh_entry = newlist;
2795 curhead->uh_flags = new_flags;
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01002796 if ((old_flags & UH_EMPTYBUF) && BUFEMPTY())
Bram Moolenaar071d4272004-06-13 20:20:40 +00002797 curbuf->b_ml.ml_flags |= ML_EMPTY;
2798 if (old_flags & UH_CHANGED)
2799 changed();
2800 else
Bram Moolenaar009b2592004-10-24 19:18:58 +00002801#ifdef FEAT_NETBEANS_INTG
2802 /* per netbeans undo rules, keep it as modified */
2803 if (!isNetbeansModified(curbuf))
2804#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002805 unchanged(curbuf, FALSE);
2806
2807 /*
2808 * restore marks from before undo/redo
2809 */
2810 for (i = 0; i < NMARKS; ++i)
Bram Moolenaarf65aad52015-02-17 13:43:40 +01002811 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002812 if (curhead->uh_namedm[i].lnum != 0)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002813 curbuf->b_namedm[i] = curhead->uh_namedm[i];
Bram Moolenaarf65aad52015-02-17 13:43:40 +01002814 if (namedm[i].lnum != 0)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002815 curhead->uh_namedm[i] = namedm[i];
Bram Moolenaarf65aad52015-02-17 13:43:40 +01002816 else
2817 curhead->uh_namedm[i].lnum = 0;
2818 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002819 if (curhead->uh_visual.vi_start.lnum != 0)
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002820 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002821 curbuf->b_visual = curhead->uh_visual;
2822 curhead->uh_visual = visualinfo;
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002823 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002824
2825 /*
2826 * If the cursor is only off by one line, put it at the same position as
2827 * before starting the change (for the "o" command).
2828 * Otherwise the cursor should go to the first undone line.
2829 */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002830 if (curhead->uh_cursor.lnum + 1 == curwin->w_cursor.lnum
Bram Moolenaar071d4272004-06-13 20:20:40 +00002831 && curwin->w_cursor.lnum > 1)
2832 --curwin->w_cursor.lnum;
Bram Moolenaar0390ded2010-08-07 12:54:12 +02002833 if (curwin->w_cursor.lnum <= curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002834 {
Bram Moolenaar0390ded2010-08-07 12:54:12 +02002835 if (curhead->uh_cursor.lnum == curwin->w_cursor.lnum)
2836 {
2837 curwin->w_cursor.col = curhead->uh_cursor.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002838#ifdef FEAT_VIRTUALEDIT
Bram Moolenaar0390ded2010-08-07 12:54:12 +02002839 if (virtual_active() && curhead->uh_cursor_vcol >= 0)
2840 coladvance((colnr_T)curhead->uh_cursor_vcol);
2841 else
2842 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002843#endif
Bram Moolenaar0390ded2010-08-07 12:54:12 +02002844 }
2845 else
2846 beginline(BL_SOL | BL_FIX);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002847 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002848 else
2849 {
2850 /* We get here with the current cursor line being past the end (eg
2851 * after adding lines at the end of the file, and then undoing it).
2852 * check_cursor() will move the cursor to the last line. Move it to
2853 * the first column here. */
2854 curwin->w_cursor.col = 0;
2855#ifdef FEAT_VIRTUALEDIT
2856 curwin->w_cursor.coladd = 0;
2857#endif
2858 }
2859
2860 /* Make sure the cursor is on an existing line and column. */
2861 check_cursor();
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002862
2863 /* Remember where we are for "g-" and ":earlier 10s". */
2864 curbuf->b_u_seq_cur = curhead->uh_seq;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002865 if (undo)
Bram Moolenaar80eaddd2017-11-11 23:37:08 +01002866 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002867 /* We are below the previous undo. However, to make ":earlier 1s"
2868 * work we compute this as being just above the just undone change. */
Bram Moolenaar80eaddd2017-11-11 23:37:08 +01002869 if (curhead->uh_next.ptr != NULL)
2870 curbuf->b_u_seq_cur = curhead->uh_next.ptr->uh_seq;
2871 else
2872 curbuf->b_u_seq_cur = 0;
2873 }
Bram Moolenaarca003e12006-03-17 23:19:38 +00002874
Bram Moolenaar730cde92010-06-27 05:18:54 +02002875 /* Remember where we are for ":earlier 1f" and ":later 1f". */
2876 if (curhead->uh_save_nr != 0)
2877 {
2878 if (undo)
2879 curbuf->b_u_save_nr_cur = curhead->uh_save_nr - 1;
2880 else
2881 curbuf->b_u_save_nr_cur = curhead->uh_save_nr;
2882 }
2883
Bram Moolenaarca003e12006-03-17 23:19:38 +00002884 /* The timestamp can be the same for multiple changes, just use the one of
2885 * the undone/redone change. */
Bram Moolenaara800b422010-06-27 01:15:55 +02002886 curbuf->b_u_time_cur = curhead->uh_time;
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002887
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002888 unblock_autocmds();
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002889#ifdef U_DEBUG
2890 u_check(FALSE);
2891#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002892}
2893
2894/*
2895 * If we deleted or added lines, report the number of less/more lines.
2896 * Otherwise, report the number of changes (this may be incorrect
2897 * in some cases, but it's better than nothing).
2898 */
2899 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002900u_undo_end(
2901 int did_undo, /* just did an undo */
2902 int absolute) /* used ":undo N" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002903{
Bram Moolenaar89d40322006-08-29 15:30:07 +00002904 char *msgstr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002905 u_header_T *uhp;
2906 char_u msgbuf[80];
Bram Moolenaar1e607892006-03-13 22:15:53 +00002907
Bram Moolenaar071d4272004-06-13 20:20:40 +00002908#ifdef FEAT_FOLDING
2909 if ((fdo_flags & FDO_UNDO) && KeyTyped)
2910 foldOpenCursor();
2911#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +00002912
2913 if (global_busy /* no messages now, wait until global is finished */
2914 || !messaging()) /* 'lazyredraw' set, don't do messages now */
2915 return;
2916
2917 if (curbuf->b_ml.ml_flags & ML_EMPTY)
2918 --u_newcount;
2919
2920 u_oldcount -= u_newcount;
2921 if (u_oldcount == -1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002922 msgstr = N_("more line");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002923 else if (u_oldcount < 0)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002924 msgstr = N_("more lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002925 else if (u_oldcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002926 msgstr = N_("line less");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002927 else if (u_oldcount > 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002928 msgstr = N_("fewer lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002929 else
2930 {
2931 u_oldcount = u_newcount;
2932 if (u_newcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002933 msgstr = N_("change");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002934 else
Bram Moolenaar89d40322006-08-29 15:30:07 +00002935 msgstr = N_("changes");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002936 }
2937
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002938 if (curbuf->b_u_curhead != NULL)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002939 {
Bram Moolenaardb552d602006-03-23 22:59:57 +00002940 /* For ":undo N" we prefer a "after #N" message. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002941 if (absolute && curbuf->b_u_curhead->uh_next.ptr != NULL)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002942 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002943 uhp = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002944 did_undo = FALSE;
2945 }
2946 else if (did_undo)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002947 uhp = curbuf->b_u_curhead;
2948 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002949 uhp = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002950 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002951 else
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002952 uhp = curbuf->b_u_newhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002953
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002954 if (uhp == NULL)
2955 *msgbuf = NUL;
2956 else
2957 u_add_time(msgbuf, sizeof(msgbuf), uhp->uh_time);
2958
Bram Moolenaarb2c03502010-07-02 20:20:09 +02002959#ifdef FEAT_CONCEAL
2960 {
2961 win_T *wp;
2962
2963 FOR_ALL_WINDOWS(wp)
2964 {
Bram Moolenaarf5963f72010-07-23 22:10:27 +02002965 if (wp->w_buffer == curbuf && wp->w_p_cole > 0)
Bram Moolenaarb2c03502010-07-02 20:20:09 +02002966 redraw_win_later(wp, NOT_VALID);
2967 }
2968 }
2969#endif
2970
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002971 smsg((char_u *)_("%ld %s; %s #%ld %s"),
Bram Moolenaarca003e12006-03-17 23:19:38 +00002972 u_oldcount < 0 ? -u_oldcount : u_oldcount,
Bram Moolenaar89d40322006-08-29 15:30:07 +00002973 _(msgstr),
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002974 did_undo ? _("before") : _("after"),
2975 uhp == NULL ? 0L : uhp->uh_seq,
2976 msgbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002977}
2978
2979/*
2980 * u_sync: stop adding to the current entry list
2981 */
2982 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002983u_sync(
2984 int force) /* Also sync when no_u_sync is set. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002985{
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002986 /* Skip it when already synced or syncing is disabled. */
2987 if (curbuf->b_u_synced || (!force && no_u_sync > 0))
2988 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002989#if defined(FEAT_XIM) && defined(FEAT_GUI_GTK)
Bram Moolenaar5c6dbcb2017-08-30 22:00:20 +02002990 if (p_imst == IM_ON_THE_SPOT && im_is_preediting())
Bram Moolenaar071d4272004-06-13 20:20:40 +00002991 return; /* XIM is busy, don't break an undo sequence */
2992#endif
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +01002993 if (get_undolevel() < 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002994 curbuf->b_u_synced = TRUE; /* no entries, nothing to do */
2995 else
2996 {
2997 u_getbot(); /* compute ue_bot of previous u_save */
2998 curbuf->b_u_curhead = NULL;
2999 }
3000}
3001
3002/*
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003003 * ":undolist": List the leafs of the undo tree
3004 */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003005 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003006ex_undolist(exarg_T *eap UNUSED)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003007{
3008 garray_T ga;
3009 u_header_T *uhp;
3010 int mark;
3011 int nomark;
3012 int changes = 1;
3013 int i;
3014
3015 /*
3016 * 1: walk the tree to find all leafs, put the info in "ga".
3017 * 2: sort the lines
3018 * 3: display the list
3019 */
3020 mark = ++lastmark;
3021 nomark = ++lastmark;
3022 ga_init2(&ga, (int)sizeof(char *), 20);
3023
3024 uhp = curbuf->b_u_oldhead;
3025 while (uhp != NULL)
3026 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003027 if (uhp->uh_prev.ptr == NULL && uhp->uh_walk != nomark
Bram Moolenaarca003e12006-03-17 23:19:38 +00003028 && uhp->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003029 {
3030 if (ga_grow(&ga, 1) == FAIL)
3031 break;
Bram Moolenaarea391762018-04-08 13:07:22 +02003032 vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7d ",
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003033 uhp->uh_seq, changes);
3034 u_add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff),
3035 uhp->uh_time);
Bram Moolenaara800b422010-06-27 01:15:55 +02003036 if (uhp->uh_save_nr > 0)
3037 {
Bram Moolenaardba01a02010-11-03 19:32:42 +01003038 while (STRLEN(IObuff) < 33)
Bram Moolenaara800b422010-06-27 01:15:55 +02003039 STRCAT(IObuff, " ");
3040 vim_snprintf_add((char *)IObuff, IOSIZE,
3041 " %3ld", uhp->uh_save_nr);
3042 }
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003043 ((char_u **)(ga.ga_data))[ga.ga_len++] = vim_strsave(IObuff);
3044 }
3045
3046 uhp->uh_walk = mark;
3047
3048 /* go down in the tree if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003049 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
3050 && uhp->uh_prev.ptr->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003051 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003052 uhp = uhp->uh_prev.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003053 ++changes;
3054 }
3055
3056 /* go to alternate branch if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003057 else if (uhp->uh_alt_next.ptr != NULL
3058 && uhp->uh_alt_next.ptr->uh_walk != nomark
3059 && uhp->uh_alt_next.ptr->uh_walk != mark)
3060 uhp = uhp->uh_alt_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003061
3062 /* go up in the tree if we haven't been there and we are at the
3063 * start of alternate branches */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003064 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
3065 && uhp->uh_next.ptr->uh_walk != nomark
3066 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003067 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003068 uhp = uhp->uh_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003069 --changes;
3070 }
3071
3072 else
3073 {
3074 /* need to backtrack; mark this node as done */
3075 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003076 if (uhp->uh_alt_prev.ptr != NULL)
3077 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003078 else
3079 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003080 uhp = uhp->uh_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003081 --changes;
3082 }
3083 }
3084 }
3085
3086 if (ga.ga_len == 0)
3087 MSG(_("Nothing to undo"));
3088 else
3089 {
3090 sort_strings((char_u **)ga.ga_data, ga.ga_len);
3091
3092 msg_start();
Bram Moolenaardba01a02010-11-03 19:32:42 +01003093 msg_puts_attr((char_u *)_("number changes when saved"),
Bram Moolenaar8820b482017-03-16 17:23:31 +01003094 HL_ATTR(HLF_T));
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003095 for (i = 0; i < ga.ga_len && !got_int; ++i)
3096 {
3097 msg_putchar('\n');
3098 if (got_int)
3099 break;
3100 msg_puts(((char_u **)ga.ga_data)[i]);
3101 }
3102 msg_end();
3103
3104 ga_clear_strings(&ga);
3105 }
3106}
3107
3108/*
3109 * Put the timestamp of an undo header in "buf[buflen]" in a nice format.
3110 */
3111 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003112u_add_time(char_u *buf, size_t buflen, time_t tt)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003113{
3114#ifdef HAVE_STRFTIME
3115 struct tm *curtime;
3116
Bram Moolenaar170b10b2016-07-29 16:15:27 +02003117 if (vim_time() - tt >= 100)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003118 {
3119 curtime = localtime(&tt);
Bram Moolenaar170b10b2016-07-29 16:15:27 +02003120 if (vim_time() - tt < (60L * 60L * 12L))
Bram Moolenaardba01a02010-11-03 19:32:42 +01003121 /* within 12 hours */
3122 (void)strftime((char *)buf, buflen, "%H:%M:%S", curtime);
Bram Moolenaardba01a02010-11-03 19:32:42 +01003123 else
Bram Moolenaarb9ce83e2012-08-23 12:59:02 +02003124 /* longer ago */
Bram Moolenaar5e3d6ca2011-01-22 21:25:11 +01003125 (void)strftime((char *)buf, buflen, "%Y/%m/%d %H:%M:%S", curtime);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003126 }
3127 else
3128#endif
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003129 vim_snprintf((char *)buf, buflen, _("%ld seconds ago"),
Bram Moolenaar170b10b2016-07-29 16:15:27 +02003130 (long)(vim_time() - tt));
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003131}
3132
3133/*
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003134 * ":undojoin": continue adding to the last entry list
3135 */
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003136 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003137ex_undojoin(exarg_T *eap UNUSED)
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003138{
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003139 if (curbuf->b_u_newhead == NULL)
3140 return; /* nothing changed before */
Bram Moolenaar57657d82006-04-21 22:12:41 +00003141 if (curbuf->b_u_curhead != NULL)
3142 {
3143 EMSG(_("E790: undojoin is not allowed after undo"));
3144 return;
3145 }
3146 if (!curbuf->b_u_synced)
3147 return; /* already unsynced */
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +01003148 if (get_undolevel() < 0)
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003149 return; /* no entries, nothing to do */
3150 else
Bram Moolenaar5e4e1b12017-01-17 22:09:45 +01003151 /* Append next change to the last entry */
3152 curbuf->b_u_synced = FALSE;
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003153}
3154
3155/*
Bram Moolenaar59f931e2010-07-24 20:27:03 +02003156 * Called after writing or reloading the file and setting b_changed to FALSE.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003157 * Now an undo means that the buffer is modified.
3158 */
3159 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003160u_unchanged(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003161{
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003162 u_unch_branch(buf->b_u_oldhead);
3163 buf->b_did_warn = FALSE;
3164}
3165
Bram Moolenaar730cde92010-06-27 05:18:54 +02003166/*
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003167 * After reloading a buffer which was saved for 'undoreload': Find the first
3168 * line that was changed and set the cursor there.
3169 */
3170 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003171u_find_first_changed(void)
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003172{
3173 u_header_T *uhp = curbuf->b_u_newhead;
3174 u_entry_T *uep;
3175 linenr_T lnum;
3176
3177 if (curbuf->b_u_curhead != NULL || uhp == NULL)
3178 return; /* undid something in an autocmd? */
3179
3180 /* Check that the last undo block was for the whole file. */
3181 uep = uhp->uh_entry;
3182 if (uep->ue_top != 0 || uep->ue_bot != 0)
3183 return;
3184
3185 for (lnum = 1; lnum < curbuf->b_ml.ml_line_count
3186 && lnum <= uep->ue_size; ++lnum)
3187 if (STRCMP(ml_get_buf(curbuf, lnum, FALSE),
3188 uep->ue_array[lnum - 1]) != 0)
3189 {
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01003190 CLEAR_POS(&(uhp->uh_cursor));
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003191 uhp->uh_cursor.lnum = lnum;
3192 return;
3193 }
3194 if (curbuf->b_ml.ml_line_count != uep->ue_size)
3195 {
3196 /* lines added or deleted at the end, put the cursor there */
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01003197 CLEAR_POS(&(uhp->uh_cursor));
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003198 uhp->uh_cursor.lnum = lnum;
3199 }
3200}
3201
3202/*
Bram Moolenaar730cde92010-06-27 05:18:54 +02003203 * Increase the write count, store it in the last undo header, what would be
3204 * used for "u".
3205 */
3206 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003207u_update_save_nr(buf_T *buf)
Bram Moolenaar730cde92010-06-27 05:18:54 +02003208{
3209 u_header_T *uhp;
3210
3211 ++buf->b_u_save_nr_last;
3212 buf->b_u_save_nr_cur = buf->b_u_save_nr_last;
3213 uhp = buf->b_u_curhead;
3214 if (uhp != NULL)
3215 uhp = uhp->uh_next.ptr;
3216 else
3217 uhp = buf->b_u_newhead;
3218 if (uhp != NULL)
3219 uhp->uh_save_nr = buf->b_u_save_nr_last;
3220}
3221
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003222 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003223u_unch_branch(u_header_T *uhp)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003224{
Bram Moolenaar1e607892006-03-13 22:15:53 +00003225 u_header_T *uh;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003226
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003227 for (uh = uhp; uh != NULL; uh = uh->uh_prev.ptr)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003228 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003229 uh->uh_flags |= UH_CHANGED;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003230 if (uh->uh_alt_next.ptr != NULL)
3231 u_unch_branch(uh->uh_alt_next.ptr); /* recursive */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003232 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003233}
3234
3235/*
3236 * Get pointer to last added entry.
3237 * If it's not valid, give an error message and return NULL.
3238 */
3239 static u_entry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003240u_get_headentry(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003241{
3242 if (curbuf->b_u_newhead == NULL || curbuf->b_u_newhead->uh_entry == NULL)
3243 {
Bram Moolenaar95f09602016-11-10 20:01:45 +01003244 IEMSG(_("E439: undo list corrupt"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003245 return NULL;
3246 }
3247 return curbuf->b_u_newhead->uh_entry;
3248}
3249
3250/*
3251 * u_getbot(): compute the line number of the previous u_save
3252 * It is called only when b_u_synced is FALSE.
3253 */
3254 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003255u_getbot(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003256{
3257 u_entry_T *uep;
3258 linenr_T extra;
3259
3260 uep = u_get_headentry(); /* check for corrupt undo list */
3261 if (uep == NULL)
3262 return;
3263
3264 uep = curbuf->b_u_newhead->uh_getbot_entry;
3265 if (uep != NULL)
3266 {
3267 /*
3268 * the new ue_bot is computed from the number of lines that has been
3269 * inserted (0 - deleted) since calling u_save. This is equal to the
3270 * old line count subtracted from the current line count.
3271 */
3272 extra = curbuf->b_ml.ml_line_count - uep->ue_lcount;
3273 uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra;
3274 if (uep->ue_bot < 1 || uep->ue_bot > curbuf->b_ml.ml_line_count)
3275 {
Bram Moolenaar95f09602016-11-10 20:01:45 +01003276 IEMSG(_("E440: undo line missing"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003277 uep->ue_bot = uep->ue_top + 1; /* assume all lines deleted, will
3278 * get all the old lines back
3279 * without deleting the current
3280 * ones */
3281 }
3282
3283 curbuf->b_u_newhead->uh_getbot_entry = NULL;
3284 }
3285
3286 curbuf->b_u_synced = TRUE;
3287}
3288
3289/*
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003290 * Free one header "uhp" and its entry list and adjust the pointers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003291 */
3292 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003293u_freeheader(
3294 buf_T *buf,
3295 u_header_T *uhp,
3296 u_header_T **uhpp) /* if not NULL reset when freeing this header */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003297{
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003298 u_header_T *uhap;
3299
Bram Moolenaar1e607892006-03-13 22:15:53 +00003300 /* When there is an alternate redo list free that branch completely,
3301 * because we can never go there. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003302 if (uhp->uh_alt_next.ptr != NULL)
3303 u_freebranch(buf, uhp->uh_alt_next.ptr, uhpp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003304
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003305 if (uhp->uh_alt_prev.ptr != NULL)
3306 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003307
Bram Moolenaar1e607892006-03-13 22:15:53 +00003308 /* Update the links in the list to remove the header. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003309 if (uhp->uh_next.ptr == NULL)
3310 buf->b_u_oldhead = uhp->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003311 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003312 uhp->uh_next.ptr->uh_prev.ptr = uhp->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003313
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003314 if (uhp->uh_prev.ptr == NULL)
3315 buf->b_u_newhead = uhp->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003316 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003317 for (uhap = uhp->uh_prev.ptr; uhap != NULL;
3318 uhap = uhap->uh_alt_next.ptr)
3319 uhap->uh_next.ptr = uhp->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003320
Bram Moolenaar1e607892006-03-13 22:15:53 +00003321 u_freeentries(buf, uhp, uhpp);
3322}
3323
3324/*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003325 * Free an alternate branch and any following alternate branches.
Bram Moolenaar1e607892006-03-13 22:15:53 +00003326 */
3327 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003328u_freebranch(
3329 buf_T *buf,
3330 u_header_T *uhp,
3331 u_header_T **uhpp) /* if not NULL reset when freeing this header */
Bram Moolenaar1e607892006-03-13 22:15:53 +00003332{
3333 u_header_T *tofree, *next;
3334
Bram Moolenaar07d06772007-11-10 21:51:15 +00003335 /* If this is the top branch we may need to use u_freeheader() to update
3336 * all the pointers. */
3337 if (uhp == buf->b_u_oldhead)
3338 {
Bram Moolenaaraa887322013-11-07 03:04:11 +01003339 while (buf->b_u_oldhead != NULL)
3340 u_freeheader(buf, buf->b_u_oldhead, uhpp);
Bram Moolenaar07d06772007-11-10 21:51:15 +00003341 return;
3342 }
3343
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003344 if (uhp->uh_alt_prev.ptr != NULL)
3345 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +00003346
3347 next = uhp;
3348 while (next != NULL)
3349 {
3350 tofree = next;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003351 if (tofree->uh_alt_next.ptr != NULL)
3352 u_freebranch(buf, tofree->uh_alt_next.ptr, uhpp); /* recursive */
3353 next = tofree->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00003354 u_freeentries(buf, tofree, uhpp);
3355 }
3356}
3357
3358/*
3359 * Free all the undo entries for one header and the header itself.
3360 * This means that "uhp" is invalid when returning.
3361 */
3362 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003363u_freeentries(
3364 buf_T *buf,
3365 u_header_T *uhp,
3366 u_header_T **uhpp) /* if not NULL reset when freeing this header */
Bram Moolenaar1e607892006-03-13 22:15:53 +00003367{
3368 u_entry_T *uep, *nuep;
3369
3370 /* Check for pointers to the header that become invalid now. */
3371 if (buf->b_u_curhead == uhp)
3372 buf->b_u_curhead = NULL;
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003373 if (buf->b_u_newhead == uhp)
3374 buf->b_u_newhead = NULL; /* freeing the newest entry */
Bram Moolenaar1e607892006-03-13 22:15:53 +00003375 if (uhpp != NULL && uhp == *uhpp)
3376 *uhpp = NULL;
3377
3378 for (uep = uhp->uh_entry; uep != NULL; uep = nuep)
3379 {
3380 nuep = uep->ue_next;
3381 u_freeentry(uep, uep->ue_size);
3382 }
3383
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003384#ifdef U_DEBUG
3385 uhp->uh_magic = 0;
3386#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003387 vim_free((char_u *)uhp);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00003388 --buf->b_u_numhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003389}
3390
3391/*
3392 * free entry 'uep' and 'n' lines in uep->ue_array[]
3393 */
3394 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003395u_freeentry(u_entry_T *uep, long n)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003396{
Bram Moolenaar8d343302005-07-12 22:46:17 +00003397 while (n > 0)
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003398 vim_free(uep->ue_array[--n]);
3399 vim_free((char_u *)uep->ue_array);
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003400#ifdef U_DEBUG
3401 uep->ue_magic = 0;
3402#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003403 vim_free((char_u *)uep);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003404}
3405
3406/*
3407 * invalidate the undo buffer; called when storage has already been released
3408 */
3409 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003410u_clearall(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003411{
3412 buf->b_u_newhead = buf->b_u_oldhead = buf->b_u_curhead = NULL;
3413 buf->b_u_synced = TRUE;
3414 buf->b_u_numhead = 0;
3415 buf->b_u_line_ptr = NULL;
3416 buf->b_u_line_lnum = 0;
3417}
3418
3419/*
3420 * save the line "lnum" for the "U" command
3421 */
3422 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003423u_saveline(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003424{
3425 if (lnum == curbuf->b_u_line_lnum) /* line is already saved */
3426 return;
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00003427 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) /* should never happen */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003428 return;
3429 u_clearline();
3430 curbuf->b_u_line_lnum = lnum;
3431 if (curwin->w_cursor.lnum == lnum)
3432 curbuf->b_u_line_colnr = curwin->w_cursor.col;
3433 else
3434 curbuf->b_u_line_colnr = 0;
3435 if ((curbuf->b_u_line_ptr = u_save_line(lnum)) == NULL)
3436 do_outofmem_msg((long_u)0);
3437}
3438
3439/*
3440 * clear the line saved for the "U" command
3441 * (this is used externally for crossing a line while in insert mode)
3442 */
3443 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003444u_clearline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003445{
3446 if (curbuf->b_u_line_ptr != NULL)
3447 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01003448 VIM_CLEAR(curbuf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003449 curbuf->b_u_line_lnum = 0;
3450 }
3451}
3452
3453/*
3454 * Implementation of the "U" command.
3455 * Differentiation from vi: "U" can be undone with the next "U".
3456 * We also allow the cursor to be in another line.
Bram Moolenaard04b7502010-07-08 22:27:55 +02003457 * Careful: may trigger autocommands that reload the buffer.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003458 */
3459 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003460u_undoline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003461{
3462 colnr_T t;
3463 char_u *oldp;
3464
3465 if (undo_off)
3466 return;
3467
Bram Moolenaare3300c82008-02-13 14:21:38 +00003468 if (curbuf->b_u_line_ptr == NULL
3469 || curbuf->b_u_line_lnum > curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003470 {
3471 beep_flush();
3472 return;
3473 }
Bram Moolenaare3300c82008-02-13 14:21:38 +00003474
3475 /* first save the line for the 'u' command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003476 if (u_savecommon(curbuf->b_u_line_lnum - 1,
Bram Moolenaar59f931e2010-07-24 20:27:03 +02003477 curbuf->b_u_line_lnum + 1, (linenr_T)0, FALSE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003478 return;
3479 oldp = u_save_line(curbuf->b_u_line_lnum);
3480 if (oldp == NULL)
3481 {
3482 do_outofmem_msg((long_u)0);
3483 return;
3484 }
3485 ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, TRUE);
3486 changed_bytes(curbuf->b_u_line_lnum, 0);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003487 vim_free(curbuf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003488 curbuf->b_u_line_ptr = oldp;
3489
3490 t = curbuf->b_u_line_colnr;
3491 if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum)
3492 curbuf->b_u_line_colnr = curwin->w_cursor.col;
3493 curwin->w_cursor.col = t;
3494 curwin->w_cursor.lnum = curbuf->b_u_line_lnum;
Bram Moolenaare3300c82008-02-13 14:21:38 +00003495 check_cursor_col();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003496}
3497
3498/*
Bram Moolenaar26a60b42005-02-22 08:49:11 +00003499 * Free all allocated memory blocks for the buffer 'buf'.
3500 */
3501 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003502u_blockfree(buf_T *buf)
Bram Moolenaar26a60b42005-02-22 08:49:11 +00003503{
Bram Moolenaar1e607892006-03-13 22:15:53 +00003504 while (buf->b_u_oldhead != NULL)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003505 u_freeheader(buf, buf->b_u_oldhead, NULL);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003506 vim_free(buf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003507}
3508
3509/*
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003510 * u_save_line(): allocate memory and copy line 'lnum' into it.
3511 * Returns NULL when out of memory.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003512 */
3513 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003514u_save_line(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003515{
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003516 return vim_strsave(ml_get(lnum));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003517}
3518
3519/*
3520 * Check if the 'modified' flag is set, or 'ff' has changed (only need to
3521 * check the first character, because it can only be "dos", "unix" or "mac").
3522 * "nofile" and "scratch" type buffers are considered to always be unchanged.
Bram Moolenaarf405c8f2017-12-09 19:51:49 +01003523 * Also considers a buffer changed when a terminal window contains a running
3524 * job.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003525 */
3526 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003527bufIsChanged(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003528{
Bram Moolenaareb44a682017-08-03 22:44:55 +02003529#ifdef FEAT_TERMINAL
3530 if (term_job_running(buf->b_term))
3531 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003532#endif
Bram Moolenaarf405c8f2017-12-09 19:51:49 +01003533 return bufIsChangedNotTerm(buf);
3534}
3535
3536/*
3537 * Like bufIsChanged() but ignoring a terminal window.
3538 */
3539 int
3540bufIsChangedNotTerm(buf_T *buf)
3541{
Bram Moolenaar4551c0a2018-06-20 22:38:21 +02003542 // In a "prompt" buffer we do respect 'modified', so that we can control
3543 // closing the window by setting or resetting that option.
3544 return (!bt_dontwrite(buf) || bt_prompt(buf))
Bram Moolenaareb44a682017-08-03 22:44:55 +02003545 && (buf->b_changed || file_ff_differs(buf, TRUE));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003546}
3547
3548 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003549curbufIsChanged(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003550{
Bram Moolenaareb44a682017-08-03 22:44:55 +02003551 return bufIsChanged(curbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003552}
Bram Moolenaara800b422010-06-27 01:15:55 +02003553
3554#if defined(FEAT_EVAL) || defined(PROTO)
3555/*
3556 * For undotree(): Append the list of undo blocks at "first_uhp" to "list".
3557 * Recursive.
3558 */
3559 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003560u_eval_tree(u_header_T *first_uhp, list_T *list)
Bram Moolenaara800b422010-06-27 01:15:55 +02003561{
3562 u_header_T *uhp = first_uhp;
3563 dict_T *dict;
3564
3565 while (uhp != NULL)
3566 {
3567 dict = dict_alloc();
3568 if (dict == NULL)
3569 return;
3570 dict_add_nr_str(dict, "seq", uhp->uh_seq, NULL);
Bram Moolenaarb2c03502010-07-02 20:20:09 +02003571 dict_add_nr_str(dict, "time", (long)uhp->uh_time, NULL);
Bram Moolenaara800b422010-06-27 01:15:55 +02003572 if (uhp == curbuf->b_u_newhead)
3573 dict_add_nr_str(dict, "newhead", 1, NULL);
3574 if (uhp == curbuf->b_u_curhead)
3575 dict_add_nr_str(dict, "curhead", 1, NULL);
3576 if (uhp->uh_save_nr > 0)
3577 dict_add_nr_str(dict, "save", uhp->uh_save_nr, NULL);
3578
3579 if (uhp->uh_alt_next.ptr != NULL)
3580 {
3581 list_T *alt_list = list_alloc();
3582
3583 if (alt_list != NULL)
3584 {
3585 /* Recursive call to add alternate undo tree. */
3586 u_eval_tree(uhp->uh_alt_next.ptr, alt_list);
3587 dict_add_list(dict, "alt", alt_list);
3588 }
3589 }
3590
3591 list_append_dict(list, dict);
3592 uhp = uhp->uh_prev.ptr;
3593 }
3594}
3595#endif