blob: ae1db43d09d7715c3fabedbb6e772e95877ac22a [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 Moolenaare38eab22019-12-05 21:50:01 +010078// 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
Bram Moolenaarfecb6602007-10-01 20:54:15 +000083
Bram Moolenaare38eab22019-12-05 21:50:01 +010084// Size of buffer used for encryption.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +020085#define CRYPT_BUF_SIZE 8192
86
Bram Moolenaar071d4272004-06-13 20:20:40 +000087#include "vim.h"
88
Bram Moolenaare38eab22019-12-05 21:50:01 +010089// Structure passed around between functions.
90// Avoids passing cryptstate_T when encryption not available.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +020091typedef struct {
92 buf_T *bi_buf;
93 FILE *bi_fp;
94#ifdef FEAT_CRYPT
95 cryptstate_T *bi_state;
Bram Moolenaare38eab22019-12-05 21:50:01 +010096 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
Bram Moolenaar8f4ac012014-08-10 13:38:34 +020099#endif
100} bufinfo_T;
101
102
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100103static void u_unch_branch(u_header_T *uhp);
104static u_entry_T *u_get_headentry(void);
105static void u_getbot(void);
106static void u_doit(int count);
107static void u_undoredo(int undo);
108static void u_undo_end(int did_undo, int absolute);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100109static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);
110static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);
111static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);
112static void u_freeentry(u_entry_T *, long);
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200113#ifdef FEAT_PERSISTENT_UNDO
Bram Moolenaar1d6fbe62016-02-19 22:46:34 +0100114# ifdef FEAT_CRYPT
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100115static int undo_flush(bufinfo_T *bi);
Bram Moolenaar1d6fbe62016-02-19 22:46:34 +0100116# endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100117static int undo_read(bufinfo_T *bi, char_u *buffer, size_t size);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100118static int serialize_uep(bufinfo_T *bi, u_entry_T *uep);
119static u_entry_T *unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name);
120static void serialize_pos(bufinfo_T *bi, pos_T pos);
121static void unserialize_pos(bufinfo_T *bi, pos_T *pos);
122static void serialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);
123static void unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info);
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200124#endif
Bram Moolenaar5843f5f2019-08-20 20:13:45 +0200125static void u_saveline(linenr_T lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000126
Bram Moolenaar18a4ba22019-05-24 19:39:03 +0200127#define U_ALLOC_LINE(size) lalloc(size, FALSE)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000128
Bram Moolenaare38eab22019-12-05 21:50:01 +0100129// used in undo_end() to report number of added and deleted lines
Bram Moolenaar071d4272004-06-13 20:20:40 +0000130static long u_newcount, u_oldcount;
131
132/*
133 * When 'u' flag included in 'cpoptions', we behave like vi. Need to remember
134 * the action that "u" should do.
135 */
136static int undo_undoes = FALSE;
137
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200138static int lastmark = 0;
139
Bram Moolenaar9db58062010-05-29 20:33:07 +0200140#if defined(U_DEBUG) || defined(PROTO)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000141/*
142 * Check the undo structures for being valid. Print a warning when something
143 * looks wrong.
144 */
145static int seen_b_u_curhead;
146static int seen_b_u_newhead;
147static int header_count;
148
149 static void
150u_check_tree(u_header_T *uhp,
151 u_header_T *exp_uh_next,
152 u_header_T *exp_uh_alt_prev)
153{
154 u_entry_T *uep;
155
156 if (uhp == NULL)
157 return;
158 ++header_count;
159 if (uhp == curbuf->b_u_curhead && ++seen_b_u_curhead > 1)
160 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100161 emsg("b_u_curhead found twice (looping?)");
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000162 return;
163 }
164 if (uhp == curbuf->b_u_newhead && ++seen_b_u_newhead > 1)
165 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100166 emsg("b_u_newhead found twice (looping?)");
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000167 return;
168 }
169
170 if (uhp->uh_magic != UH_MAGIC)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100171 emsg("uh_magic wrong (may be using freed memory)");
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000172 else
173 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100174 // Check pointers back are correct.
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200175 if (uhp->uh_next.ptr != exp_uh_next)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000176 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100177 emsg("uh_next wrong");
178 smsg("expected: 0x%x, actual: 0x%x",
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200179 exp_uh_next, uhp->uh_next.ptr);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000180 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200181 if (uhp->uh_alt_prev.ptr != exp_uh_alt_prev)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000182 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100183 emsg("uh_alt_prev wrong");
184 smsg("expected: 0x%x, actual: 0x%x",
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200185 exp_uh_alt_prev, uhp->uh_alt_prev.ptr);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000186 }
187
Bram Moolenaare38eab22019-12-05 21:50:01 +0100188 // Check the undo tree at this header.
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000189 for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
190 {
191 if (uep->ue_magic != UE_MAGIC)
192 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100193 emsg("ue_magic wrong (may be using freed memory)");
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000194 break;
195 }
196 }
197
Bram Moolenaare38eab22019-12-05 21:50:01 +0100198 // Check the next alt tree.
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200199 u_check_tree(uhp->uh_alt_next.ptr, uhp->uh_next.ptr, uhp);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000200
Bram Moolenaare38eab22019-12-05 21:50:01 +0100201 // Check the next header in this branch.
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200202 u_check_tree(uhp->uh_prev.ptr, uhp, NULL);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000203 }
204}
205
Bram Moolenaarb0b50882010-07-07 18:26:28 +0200206 static void
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000207u_check(int newhead_may_be_NULL)
208{
209 seen_b_u_newhead = 0;
210 seen_b_u_curhead = 0;
211 header_count = 0;
212
213 u_check_tree(curbuf->b_u_oldhead, NULL, NULL);
214
215 if (seen_b_u_newhead == 0 && curbuf->b_u_oldhead != NULL
216 && !(newhead_may_be_NULL && curbuf->b_u_newhead == NULL))
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100217 semsg("b_u_newhead invalid: 0x%x", curbuf->b_u_newhead);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000218 if (curbuf->b_u_curhead != NULL && seen_b_u_curhead == 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100219 semsg("b_u_curhead invalid: 0x%x", curbuf->b_u_curhead);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000220 if (header_count != curbuf->b_u_numhead)
221 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100222 emsg("b_u_numhead invalid");
223 smsg("expected: %ld, actual: %ld",
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000224 (long)header_count, (long)curbuf->b_u_numhead);
225 }
226}
227#endif
228
Bram Moolenaar071d4272004-06-13 20:20:40 +0000229/*
Bram Moolenaard857f0e2005-06-21 22:37:39 +0000230 * Save the current line for both the "u" and "U" command.
Bram Moolenaar687a29c2013-04-15 15:47:12 +0200231 * Careful: may trigger autocommands that reload the buffer.
Bram Moolenaard857f0e2005-06-21 22:37:39 +0000232 * Returns OK or FAIL.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000233 */
234 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100235u_save_cursor(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000236{
237 return (u_save((linenr_T)(curwin->w_cursor.lnum - 1),
238 (linenr_T)(curwin->w_cursor.lnum + 1)));
239}
240
241/*
242 * Save the lines between "top" and "bot" for both the "u" and "U" command.
Bram Moolenaar739f13a2021-12-13 13:12:53 +0000243 * "top" may be 0 and "bot" may be curbuf->b_ml.ml_line_count + 1.
Bram Moolenaard04b7502010-07-08 22:27:55 +0200244 * Careful: may trigger autocommands that reload the buffer.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000245 * Returns FAIL when lines could not be saved, OK otherwise.
246 */
247 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100248u_save(linenr_T top, linenr_T bot)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000249{
250 if (undo_off)
251 return OK;
252
Bram Moolenaarefc81332018-07-13 16:31:19 +0200253 if (top >= bot || bot > curbuf->b_ml.ml_line_count + 1)
254 return FAIL; // rely on caller to give an error message
Bram Moolenaar071d4272004-06-13 20:20:40 +0000255
256 if (top + 2 == bot)
257 u_saveline((linenr_T)(top + 1));
258
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200259 return (u_savecommon(top, bot, (linenr_T)0, FALSE));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000260}
261
262/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200263 * Save the line "lnum" (used by ":s" and "~" command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000264 * The line is replaced, so the new bottom line is lnum + 1.
Bram Moolenaard04b7502010-07-08 22:27:55 +0200265 * Careful: may trigger autocommands that reload the buffer.
266 * Returns FAIL when lines could not be saved, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000267 */
268 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100269u_savesub(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000270{
271 if (undo_off)
272 return OK;
273
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200274 return (u_savecommon(lnum - 1, lnum + 1, lnum + 1, FALSE));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000275}
276
277/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200278 * A new line is inserted before line "lnum" (used by :s command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000279 * The line is inserted, so the new bottom line is lnum + 1.
Bram Moolenaard04b7502010-07-08 22:27:55 +0200280 * Careful: may trigger autocommands that reload the buffer.
281 * Returns FAIL when lines could not be saved, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000282 */
283 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100284u_inssub(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000285{
286 if (undo_off)
287 return OK;
288
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200289 return (u_savecommon(lnum - 1, lnum, lnum + 1, FALSE));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000290}
291
292/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200293 * Save the lines "lnum" - "lnum" + nlines (used by delete command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000294 * The lines are deleted, so the new bottom line is lnum, unless the buffer
295 * becomes empty.
Bram Moolenaard04b7502010-07-08 22:27:55 +0200296 * Careful: may trigger autocommands that reload the buffer.
297 * Returns FAIL when lines could not be saved, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000298 */
299 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100300u_savedel(linenr_T lnum, long nlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000301{
302 if (undo_off)
303 return OK;
304
305 return (u_savecommon(lnum - 1, lnum + nlines,
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200306 nlines == curbuf->b_ml.ml_line_count ? 2 : lnum, FALSE));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000307}
308
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000309/*
310 * Return TRUE when undo is allowed. Otherwise give an error message and
311 * return FALSE.
312 */
Bram Moolenaarce6ef252006-07-12 19:49:41 +0000313 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100314undo_allowed(void)
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000315{
Bram Moolenaare38eab22019-12-05 21:50:01 +0100316 // Don't allow changes when 'modifiable' is off.
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000317 if (!curbuf->b_p_ma)
318 {
Bram Moolenaar108010a2021-06-27 22:03:33 +0200319 emsg(_(e_cannot_make_changes_modifiable_is_off));
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000320 return FALSE;
321 }
322
323#ifdef HAVE_SANDBOX
Bram Moolenaare38eab22019-12-05 21:50:01 +0100324 // In the sandbox it's not allowed to change the text.
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000325 if (sandbox != 0)
326 {
Bram Moolenaard8e44472021-07-21 22:20:33 +0200327 emsg(_(e_not_allowed_in_sandbox));
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000328 return FALSE;
329 }
330#endif
331
Bram Moolenaare38eab22019-12-05 21:50:01 +0100332 // Don't allow changes in the buffer while editing the cmdline. The
333 // caller of getcmdline() may get confused.
Bram Moolenaar6adb9ea2020-04-30 22:31:18 +0200334 if (textwinlock != 0 || textlock != 0)
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000335 {
Bram Moolenaarff06f282020-04-21 22:01:14 +0200336 emsg(_(e_textlock));
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000337 return FALSE;
338 }
339
340 return TRUE;
341}
342
Bram Moolenaarb0b50882010-07-07 18:26:28 +0200343/*
Bram Moolenaar32aa1022019-11-02 22:54:41 +0100344 * Get the undolevel value for the current buffer.
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +0100345 */
346 static long
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100347get_undolevel(void)
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +0100348{
349 if (curbuf->b_p_ul == NO_LOCAL_UNDOLEVEL)
350 return p_ul;
351 return curbuf->b_p_ul;
352}
353
354/*
Bram Moolenaarccae4672019-01-04 15:09:57 +0100355 * u_save_line(): save an allocated copy of line "lnum" into "ul".
356 * Returns FAIL when out of memory.
357 */
358 static int
359u_save_line(undoline_T *ul, linenr_T lnum)
360{
361 char_u *line = ml_get(lnum);
362
363 if (curbuf->b_ml.ml_line_len == 0)
364 {
365 ul->ul_len = 1;
366 ul->ul_line = vim_strsave((char_u *)"");
367 }
368 else
369 {
Bram Moolenaar964b3742019-05-24 18:54:09 +0200370 // This uses the length in the memline, thus text properties are
371 // included.
Bram Moolenaarccae4672019-01-04 15:09:57 +0100372 ul->ul_len = curbuf->b_ml.ml_line_len;
373 ul->ul_line = vim_memsave(line, ul->ul_len);
374 }
375 return ul->ul_line == NULL ? FAIL : OK;
376}
377
Bram Moolenaard5c2c772020-05-30 16:17:33 +0200378#ifdef FEAT_PROP_POPUP
Bram Moolenaarccae4672019-01-04 15:09:57 +0100379/*
Bram Moolenaara9d4b842020-05-30 14:46:52 +0200380 * return TRUE if line "lnum" has text property "flags".
381 */
382 static int
383has_prop_w_flags(linenr_T lnum, int flags)
384{
385 char_u *props;
386 int i;
387 int proplen = get_text_props(curbuf, lnum, &props, FALSE);
388
389 for (i = 0; i < proplen; ++i)
390 {
391 textprop_T prop;
392
393 mch_memmove(&prop, props + i * sizeof prop, sizeof prop);
394 if (prop.tp_flags & flags)
395 return TRUE;
396 }
397 return FALSE;
398}
Bram Moolenaard5c2c772020-05-30 16:17:33 +0200399#endif
Bram Moolenaara9d4b842020-05-30 14:46:52 +0200400
401/*
Bram Moolenaarb0b50882010-07-07 18:26:28 +0200402 * Common code for various ways to save text before a change.
Bram Moolenaard04b7502010-07-08 22:27:55 +0200403 * "top" is the line above the first changed line.
404 * "bot" is the line below the last changed line.
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200405 * "newbot" is the new bottom line. Use zero when not known.
406 * "reload" is TRUE when saving for a buffer reload.
Bram Moolenaard04b7502010-07-08 22:27:55 +0200407 * Careful: may trigger autocommands that reload the buffer.
408 * Returns FAIL when lines could not be saved, OK otherwise.
Bram Moolenaarb0b50882010-07-07 18:26:28 +0200409 */
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200410 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100411u_savecommon(
412 linenr_T top,
413 linenr_T bot,
414 linenr_T newbot,
415 int reload)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000416{
Bram Moolenaar1e607892006-03-13 22:15:53 +0000417 linenr_T lnum;
418 long i;
419 u_header_T *uhp;
420 u_header_T *old_curhead;
421 u_entry_T *uep;
422 u_entry_T *prev_uep;
423 long size;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000424
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200425 if (!reload)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000426 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100427 // When making changes is not allowed return FAIL. It's a crude way
428 // to make all change commands fail.
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200429 if (!undo_allowed())
Bram Moolenaar009b2592004-10-24 19:18:58 +0000430 return FAIL;
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200431
432#ifdef FEAT_NETBEANS_INTG
433 /*
434 * Netbeans defines areas that cannot be modified. Bail out here when
435 * trying to change text in a guarded area.
436 */
437 if (netbeans_active())
Bram Moolenaar009b2592004-10-24 19:18:58 +0000438 {
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200439 if (netbeans_is_guarded(top, bot))
440 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100441 emsg(_(e_guarded));
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200442 return FAIL;
443 }
444 if (curbuf->b_p_ro)
445 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100446 emsg(_(e_nbreadonly));
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200447 return FAIL;
448 }
Bram Moolenaar009b2592004-10-24 19:18:58 +0000449 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000450#endif
Bram Moolenaar63ecdda2017-07-28 22:29:35 +0200451#ifdef FEAT_TERMINAL
Bram Moolenaare38eab22019-12-05 21:50:01 +0100452 // A change in a terminal buffer removes the highlighting.
Bram Moolenaar63ecdda2017-07-28 22:29:35 +0200453 term_change_in_curbuf();
454#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000455
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200456 /*
457 * Saving text for undo means we are going to make a change. Give a
458 * warning for a read-only file before making the change, so that the
459 * FileChangedRO event can replace the buffer with a read-write version
460 * (e.g., obtained from a source control system).
461 */
462 change_warning(0);
463 if (bot > curbuf->b_ml.ml_line_count + 1)
464 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100465 // This happens when the FileChangedRO autocommand changes the
466 // file in a way it becomes shorter.
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100467 emsg(_("E881: Line count changed unexpectedly"));
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200468 return FAIL;
469 }
Bram Moolenaard04b7502010-07-08 22:27:55 +0200470 }
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200471
472#ifdef U_DEBUG
473 u_check(FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000474#endif
475
Bram Moolenaara9d4b842020-05-30 14:46:52 +0200476#ifdef FEAT_PROP_POPUP
477 // Include the line above if a text property continues from it.
478 // Include the line below if a text property continues to it.
479 if (bot - top > 1)
480 {
481 if (top > 0 && has_prop_w_flags(top + 1, TP_FLAG_CONT_PREV))
482 --top;
483 if (bot <= curbuf->b_ml.ml_line_count
484 && has_prop_w_flags(bot - 1, TP_FLAG_CONT_NEXT))
485 {
486 ++bot;
487 if (newbot != 0)
488 ++newbot;
489 }
490 }
491#endif
492
Bram Moolenaar071d4272004-06-13 20:20:40 +0000493 size = bot - top - 1;
494
495 /*
Bram Moolenaarb0b50882010-07-07 18:26:28 +0200496 * If curbuf->b_u_synced == TRUE make a new header.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000497 */
498 if (curbuf->b_u_synced)
499 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100500 // Need to create new entry in b_changelist.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000501 curbuf->b_new_change = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000502
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +0100503 if (get_undolevel() >= 0)
Bram Moolenaar1e607892006-03-13 22:15:53 +0000504 {
505 /*
506 * Make a new header entry. Do this first so that we don't mess
507 * up the undo info when out of memory.
508 */
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200509 uhp = U_ALLOC_LINE(sizeof(u_header_T));
Bram Moolenaar1e607892006-03-13 22:15:53 +0000510 if (uhp == NULL)
511 goto nomem;
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000512#ifdef U_DEBUG
513 uhp->uh_magic = UH_MAGIC;
514#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +0000515 }
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000516 else
517 uhp = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000518
Bram Moolenaar071d4272004-06-13 20:20:40 +0000519 /*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000520 * If we undid more than we redid, move the entry lists before and
521 * including curbuf->b_u_curhead to an alternate branch.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000522 */
Bram Moolenaar1e607892006-03-13 22:15:53 +0000523 old_curhead = curbuf->b_u_curhead;
524 if (old_curhead != NULL)
525 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200526 curbuf->b_u_newhead = old_curhead->uh_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000527 curbuf->b_u_curhead = NULL;
528 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000529
530 /*
531 * free headers to keep the size right
532 */
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +0100533 while (curbuf->b_u_numhead > get_undolevel()
534 && curbuf->b_u_oldhead != NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +0000535 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000536 u_header_T *uhfree = curbuf->b_u_oldhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000537
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000538 if (uhfree == old_curhead)
Bram Moolenaare38eab22019-12-05 21:50:01 +0100539 // Can't reconnect the branch, delete all of it.
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000540 u_freebranch(curbuf, uhfree, &old_curhead);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200541 else if (uhfree->uh_alt_next.ptr == NULL)
Bram Moolenaare38eab22019-12-05 21:50:01 +0100542 // There is no branch, only free one header.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000543 u_freeheader(curbuf, uhfree, &old_curhead);
Bram Moolenaar1e607892006-03-13 22:15:53 +0000544 else
545 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100546 // Free the oldest alternate branch as a whole.
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200547 while (uhfree->uh_alt_next.ptr != NULL)
548 uhfree = uhfree->uh_alt_next.ptr;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000549 u_freebranch(curbuf, uhfree, &old_curhead);
Bram Moolenaar1e607892006-03-13 22:15:53 +0000550 }
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000551#ifdef U_DEBUG
552 u_check(TRUE);
553#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +0000554 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000555
Bram Moolenaare38eab22019-12-05 21:50:01 +0100556 if (uhp == NULL) // no undo at all
Bram Moolenaar071d4272004-06-13 20:20:40 +0000557 {
Bram Moolenaar1e607892006-03-13 22:15:53 +0000558 if (old_curhead != NULL)
559 u_freebranch(curbuf, old_curhead, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000560 curbuf->b_u_synced = FALSE;
561 return OK;
562 }
563
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200564 uhp->uh_prev.ptr = NULL;
565 uhp->uh_next.ptr = curbuf->b_u_newhead;
566 uhp->uh_alt_next.ptr = old_curhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000567 if (old_curhead != NULL)
568 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200569 uhp->uh_alt_prev.ptr = old_curhead->uh_alt_prev.ptr;
570 if (uhp->uh_alt_prev.ptr != NULL)
571 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = uhp;
572 old_curhead->uh_alt_prev.ptr = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000573 if (curbuf->b_u_oldhead == old_curhead)
574 curbuf->b_u_oldhead = uhp;
575 }
Bram Moolenaar89ed3df2007-01-09 19:23:12 +0000576 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200577 uhp->uh_alt_prev.ptr = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000578 if (curbuf->b_u_newhead != NULL)
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200579 curbuf->b_u_newhead->uh_prev.ptr = uhp;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000580
Bram Moolenaarca003e12006-03-17 23:19:38 +0000581 uhp->uh_seq = ++curbuf->b_u_seq_last;
582 curbuf->b_u_seq_cur = uhp->uh_seq;
Bram Moolenaar170b10b2016-07-29 16:15:27 +0200583 uhp->uh_time = vim_time();
Bram Moolenaara800b422010-06-27 01:15:55 +0200584 uhp->uh_save_nr = 0;
585 curbuf->b_u_time_cur = uhp->uh_time + 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000586
Bram Moolenaar1e607892006-03-13 22:15:53 +0000587 uhp->uh_walk = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000588 uhp->uh_entry = NULL;
589 uhp->uh_getbot_entry = NULL;
Bram Moolenaare38eab22019-12-05 21:50:01 +0100590 uhp->uh_cursor = curwin->w_cursor; // save cursor pos. for undo
Bram Moolenaar071d4272004-06-13 20:20:40 +0000591 if (virtual_active() && curwin->w_cursor.coladd > 0)
592 uhp->uh_cursor_vcol = getviscol();
593 else
594 uhp->uh_cursor_vcol = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000595
Bram Moolenaare38eab22019-12-05 21:50:01 +0100596 // save changed and buffer empty flag for undo
Bram Moolenaar071d4272004-06-13 20:20:40 +0000597 uhp->uh_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
598 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
599
Bram Moolenaare38eab22019-12-05 21:50:01 +0100600 // save named marks and Visual marks for undo
Bram Moolenaar071d4272004-06-13 20:20:40 +0000601 mch_memmove(uhp->uh_namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000602 uhp->uh_visual = curbuf->b_visual;
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000603
Bram Moolenaar071d4272004-06-13 20:20:40 +0000604 curbuf->b_u_newhead = uhp;
605 if (curbuf->b_u_oldhead == NULL)
606 curbuf->b_u_oldhead = uhp;
607 ++curbuf->b_u_numhead;
608 }
609 else
610 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100611 if (get_undolevel() < 0) // no undo at all
Bram Moolenaar071d4272004-06-13 20:20:40 +0000612 return OK;
613
614 /*
615 * When saving a single line, and it has been saved just before, it
616 * doesn't make sense saving it again. Saves a lot of memory when
617 * making lots of changes inside the same line.
618 * This is only possible if the previous change didn't increase or
619 * decrease the number of lines.
620 * Check the ten last changes. More doesn't make sense and takes too
621 * long.
622 */
623 if (size == 1)
624 {
625 uep = u_get_headentry();
626 prev_uep = NULL;
627 for (i = 0; i < 10; ++i)
628 {
629 if (uep == NULL)
630 break;
631
Bram Moolenaare38eab22019-12-05 21:50:01 +0100632 // If lines have been inserted/deleted we give up.
633 // Also when the line was included in a multi-line save.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000634 if ((curbuf->b_u_newhead->uh_getbot_entry != uep
635 ? (uep->ue_top + uep->ue_size + 1
636 != (uep->ue_bot == 0
637 ? curbuf->b_ml.ml_line_count + 1
638 : uep->ue_bot))
639 : uep->ue_lcount != curbuf->b_ml.ml_line_count)
640 || (uep->ue_size > 1
641 && top >= uep->ue_top
642 && top + 2 <= uep->ue_top + uep->ue_size + 1))
643 break;
644
Bram Moolenaare38eab22019-12-05 21:50:01 +0100645 // If it's the same line we can skip saving it again.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000646 if (uep->ue_size == 1 && uep->ue_top == top)
647 {
648 if (i > 0)
649 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100650 // It's not the last entry: get ue_bot for the last
651 // entry now. Following deleted/inserted lines go to
652 // the re-used entry.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000653 u_getbot();
654 curbuf->b_u_synced = FALSE;
655
Bram Moolenaare38eab22019-12-05 21:50:01 +0100656 // Move the found entry to become the last entry. The
657 // order of undo/redo doesn't matter for the entries
658 // we move it over, since they don't change the line
659 // count and don't include this line. It does matter
660 // for the found entry if the line count is changed by
661 // the executed command.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000662 prev_uep->ue_next = uep->ue_next;
663 uep->ue_next = curbuf->b_u_newhead->uh_entry;
664 curbuf->b_u_newhead->uh_entry = uep;
665 }
666
Bram Moolenaare38eab22019-12-05 21:50:01 +0100667 // The executed command may change the line count.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000668 if (newbot != 0)
669 uep->ue_bot = newbot;
670 else if (bot > curbuf->b_ml.ml_line_count)
671 uep->ue_bot = 0;
672 else
673 {
674 uep->ue_lcount = curbuf->b_ml.ml_line_count;
675 curbuf->b_u_newhead->uh_getbot_entry = uep;
676 }
677 return OK;
678 }
679 prev_uep = uep;
680 uep = uep->ue_next;
681 }
682 }
683
Bram Moolenaare38eab22019-12-05 21:50:01 +0100684 // find line number for ue_bot for previous u_save()
Bram Moolenaar071d4272004-06-13 20:20:40 +0000685 u_getbot();
686 }
687
Bram Moolenaar4f974752019-02-17 17:44:42 +0100688#if !defined(UNIX) && !defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000689 /*
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100690 * With Amiga we can't handle big undo's, because
Bram Moolenaar071d4272004-06-13 20:20:40 +0000691 * then u_alloc_line would have to allocate a block larger than 32K
692 */
693 if (size >= 8000)
694 goto nomem;
695#endif
696
697 /*
698 * add lines in front of entry list
699 */
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200700 uep = U_ALLOC_LINE(sizeof(u_entry_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000701 if (uep == NULL)
702 goto nomem;
Bram Moolenaara80faa82020-04-12 19:37:17 +0200703 CLEAR_POINTER(uep);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000704#ifdef U_DEBUG
705 uep->ue_magic = UE_MAGIC;
706#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000707
708 uep->ue_size = size;
709 uep->ue_top = top;
710 if (newbot != 0)
711 uep->ue_bot = newbot;
712 /*
713 * Use 0 for ue_bot if bot is below last line.
714 * Otherwise we have to compute ue_bot later.
715 */
716 else if (bot > curbuf->b_ml.ml_line_count)
717 uep->ue_bot = 0;
718 else
719 {
720 uep->ue_lcount = curbuf->b_ml.ml_line_count;
721 curbuf->b_u_newhead->uh_getbot_entry = uep;
722 }
723
Bram Moolenaar26a60b42005-02-22 08:49:11 +0000724 if (size > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000725 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200726 if ((uep->ue_array = U_ALLOC_LINE(sizeof(undoline_T) * size)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000727 {
728 u_freeentry(uep, 0L);
729 goto nomem;
730 }
731 for (i = 0, lnum = top + 1; i < size; ++i)
732 {
Bram Moolenaarae5bce12005-08-15 21:41:48 +0000733 fast_breakcheck();
734 if (got_int)
735 {
736 u_freeentry(uep, i);
737 return FAIL;
738 }
Bram Moolenaarccae4672019-01-04 15:09:57 +0100739 if (u_save_line(&uep->ue_array[i], lnum++) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000740 {
741 u_freeentry(uep, i);
742 goto nomem;
743 }
744 }
745 }
Bram Moolenaarf461c8e2005-06-25 23:04:51 +0000746 else
747 uep->ue_array = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000748 uep->ue_next = curbuf->b_u_newhead->uh_entry;
749 curbuf->b_u_newhead->uh_entry = uep;
750 curbuf->b_u_synced = FALSE;
751 undo_undoes = FALSE;
752
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000753#ifdef U_DEBUG
754 u_check(FALSE);
755#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000756 return OK;
757
758nomem:
Bram Moolenaare38eab22019-12-05 21:50:01 +0100759 msg_silent = 0; // must display the prompt
Bram Moolenaar071d4272004-06-13 20:20:40 +0000760 if (ask_yesno((char_u *)_("No undo possible; continue anyway"), TRUE)
761 == 'y')
762 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100763 undo_off = TRUE; // will be reset when character typed
Bram Moolenaar071d4272004-06-13 20:20:40 +0000764 return OK;
765 }
766 do_outofmem_msg((long_u)0);
767 return FAIL;
768}
769
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200770#if defined(FEAT_PERSISTENT_UNDO) || defined(PROTO)
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200771
Bram Moolenaare38eab22019-12-05 21:50:01 +0100772# define UF_START_MAGIC "Vim\237UnDo\345" // magic at start of undofile
Bram Moolenaar9db58062010-05-29 20:33:07 +0200773# define UF_START_MAGIC_LEN 9
Bram Moolenaare38eab22019-12-05 21:50:01 +0100774# define UF_HEADER_MAGIC 0x5fd0 // magic at start of header
775# define UF_HEADER_END_MAGIC 0xe7aa // magic after last header
776# define UF_ENTRY_MAGIC 0xf518 // magic at start of entry
777# define UF_ENTRY_END_MAGIC 0x3581 // magic after last entry
778# define UF_VERSION 2 // 2-byte undofile version number
779# define UF_VERSION_CRYPT 0x8002 // idem, encrypted
Bram Moolenaara800b422010-06-27 01:15:55 +0200780
Bram Moolenaare38eab22019-12-05 21:50:01 +0100781// extra fields for header
Bram Moolenaara800b422010-06-27 01:15:55 +0200782# define UF_LAST_SAVE_NR 1
783
Bram Moolenaare38eab22019-12-05 21:50:01 +0100784// extra fields for uhp
Bram Moolenaara800b422010-06-27 01:15:55 +0200785# define UHP_SAVE_NR 1
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200786
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200787static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200788
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200789/*
790 * Compute the hash for the current buffer text into hash[UNDO_HASH_SIZE].
791 */
792 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100793u_compute_hash(char_u *hash)
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200794{
795 context_sha256_T ctx;
796 linenr_T lnum;
797 char_u *p;
798
799 sha256_start(&ctx);
Bram Moolenaarae7ba982011-12-08 15:14:09 +0100800 for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum)
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200801 {
802 p = ml_get(lnum);
Bram Moolenaar442b4222010-05-24 21:34:22 +0200803 sha256_update(&ctx, p, (UINT32_T)(STRLEN(p) + 1));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200804 }
805 sha256_finish(&ctx, hash);
806}
807
808/*
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200809 * Return an allocated string of the full path of the target undofile.
810 * When "reading" is TRUE find the file to read, go over all directories in
811 * 'undodir'.
812 * When "reading" is FALSE use the first name where the directory exists.
Bram Moolenaar9db58062010-05-29 20:33:07 +0200813 * Returns NULL when there is no place to write or no file to read.
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200814 */
Bram Moolenaar840d16f2019-09-10 21:27:18 +0200815 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100816u_get_undo_file_name(char_u *buf_ffname, int reading)
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200817{
818 char_u *dirp;
819 char_u dir_name[IOSIZE + 1];
820 char_u *munged_name = NULL;
821 char_u *undo_file_name = NULL;
822 int dir_len;
823 char_u *p;
Bram Moolenaar8767f522016-07-01 17:17:39 +0200824 stat_T st;
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200825 char_u *ffname = buf_ffname;
826#ifdef HAVE_READLINK
827 char_u fname_buf[MAXPATHL];
828#endif
829
830 if (ffname == NULL)
831 return NULL;
832
833#ifdef HAVE_READLINK
Bram Moolenaare38eab22019-12-05 21:50:01 +0100834 // Expand symlink in the file name, so that we put the undo file with the
835 // actual file instead of with the symlink.
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200836 if (resolve_symlink(ffname, fname_buf) == OK)
837 ffname = fname_buf;
838#endif
839
Bram Moolenaare38eab22019-12-05 21:50:01 +0100840 // Loop over 'undodir'. When reading find the first file that exists.
841 // When not reading use the first directory that exists or ".".
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200842 dirp = p_udir;
843 while (*dirp != NUL)
844 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +0200845 dir_len = copy_option_part(&dirp, dir_name, IOSIZE, ",");
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200846 if (dir_len == 1 && dir_name[0] == '.')
847 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100848 // Use same directory as the ffname,
849 // "dir/name" -> "dir/.name.un~"
Bram Moolenaar71ccd032020-06-12 22:59:11 +0200850 undo_file_name = vim_strnsave(ffname, STRLEN(ffname) + 5);
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200851 if (undo_file_name == NULL)
852 break;
853 p = gettail(undo_file_name);
Bram Moolenaar206f0112014-03-12 16:51:55 +0100854#ifdef VMS
Bram Moolenaare38eab22019-12-05 21:50:01 +0100855 // VMS can not handle more than one dot in the filenames
856 // use "dir/name" -> "dir/_un_name" - add _un_
857 // at the beginning to keep the extension
Bram Moolenaar206f0112014-03-12 16:51:55 +0100858 mch_memmove(p + 4, p, STRLEN(p) + 1);
859 mch_memmove(p, "_un_", 4);
860
861#else
Bram Moolenaare38eab22019-12-05 21:50:01 +0100862 // Use same directory as the ffname,
863 // "dir/name" -> "dir/.name.un~"
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200864 mch_memmove(p + 1, p, STRLEN(p) + 1);
865 *p = '.';
866 STRCAT(p, ".un~");
Bram Moolenaar206f0112014-03-12 16:51:55 +0100867#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200868 }
869 else
870 {
871 dir_name[dir_len] = NUL;
872 if (mch_isdir(dir_name))
873 {
874 if (munged_name == NULL)
875 {
876 munged_name = vim_strsave(ffname);
877 if (munged_name == NULL)
878 return NULL;
Bram Moolenaar91acfff2017-03-12 19:22:36 +0100879 for (p = munged_name; *p != NUL; MB_PTR_ADV(p))
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200880 if (vim_ispathsep(*p))
881 *p = '%';
882 }
883 undo_file_name = concat_fnames(dir_name, munged_name, TRUE);
884 }
885 }
886
Bram Moolenaare38eab22019-12-05 21:50:01 +0100887 // When reading check if the file exists.
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200888 if (undo_file_name != NULL && (!reading
889 || mch_stat((char *)undo_file_name, &st) >= 0))
890 break;
Bram Moolenaard23a8232018-02-10 18:45:26 +0100891 VIM_CLEAR(undo_file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200892 }
893
894 vim_free(munged_name);
895 return undo_file_name;
896}
897
Bram Moolenaar9db58062010-05-29 20:33:07 +0200898 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100899corruption_error(char *mesg, char_u *file_name)
Bram Moolenaar9db58062010-05-29 20:33:07 +0200900{
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100901 semsg(_("E825: Corrupted undo file (%s): %s"), mesg, file_name);
Bram Moolenaar9db58062010-05-29 20:33:07 +0200902}
903
904 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100905u_free_uhp(u_header_T *uhp)
Bram Moolenaar9db58062010-05-29 20:33:07 +0200906{
907 u_entry_T *nuep;
908 u_entry_T *uep;
909
910 uep = uhp->uh_entry;
911 while (uep != NULL)
912 {
913 nuep = uep->ue_next;
914 u_freeentry(uep, uep->ue_size);
915 uep = nuep;
916 }
917 vim_free(uhp);
918}
919
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200920/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200921 * Write a sequence of bytes to the undo file.
922 * Buffers and encrypts as needed.
923 * Returns OK or FAIL.
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200924 */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200925 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100926undo_write(bufinfo_T *bi, char_u *ptr, size_t len)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200927{
928#ifdef FEAT_CRYPT
929 if (bi->bi_buffer != NULL)
930 {
931 size_t len_todo = len;
932 char_u *p = ptr;
933
934 while (bi->bi_used + len_todo >= CRYPT_BUF_SIZE)
935 {
936 size_t n = CRYPT_BUF_SIZE - bi->bi_used;
937
938 mch_memmove(bi->bi_buffer + bi->bi_used, p, n);
939 len_todo -= n;
940 p += n;
941 bi->bi_used = CRYPT_BUF_SIZE;
942 if (undo_flush(bi) == FAIL)
943 return FAIL;
944 }
945 if (len_todo > 0)
946 {
947 mch_memmove(bi->bi_buffer + bi->bi_used, p, len_todo);
948 bi->bi_used += len_todo;
949 }
950 return OK;
951 }
952#endif
953 if (fwrite(ptr, len, (size_t)1, bi->bi_fp) != 1)
954 return FAIL;
955 return OK;
956}
957
958#ifdef FEAT_CRYPT
959 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100960undo_flush(bufinfo_T *bi)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200961{
Bram Moolenaar829aa642017-08-23 22:32:35 +0200962 if (bi->bi_buffer != NULL && bi->bi_state != NULL && bi->bi_used > 0)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200963 {
Christian Brabandtf573c6e2021-06-20 14:02:16 +0200964 // Last parameter is only used for sodium encryption and that
965 // explicitly disables encryption of undofiles.
966 crypt_encode_inplace(bi->bi_state, bi->bi_buffer, bi->bi_used, FALSE);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200967 if (fwrite(bi->bi_buffer, bi->bi_used, (size_t)1, bi->bi_fp) != 1)
968 return FAIL;
969 bi->bi_used = 0;
970 }
971 return OK;
972}
973#endif
974
975/*
976 * Write "ptr[len]" and crypt the bytes when needed.
977 * Returns OK or FAIL.
978 */
979 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100980fwrite_crypt(bufinfo_T *bi, char_u *ptr, size_t len)
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200981{
982#ifdef FEAT_CRYPT
983 char_u *copy;
984 char_u small_buf[100];
985 size_t i;
986
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200987 if (bi->bi_state != NULL && bi->bi_buffer == NULL)
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200988 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100989 // crypting every piece of text separately
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200990 if (len < 100)
Bram Moolenaare38eab22019-12-05 21:50:01 +0100991 copy = small_buf; // no malloc()/free() for short strings
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200992 else
993 {
994 copy = lalloc(len, FALSE);
995 if (copy == NULL)
996 return 0;
997 }
Christian Brabandtf573c6e2021-06-20 14:02:16 +0200998 // Last parameter is only used for sodium encryption and that
999 // explicitly disables encryption of undofiles.
1000 crypt_encode(bi->bi_state, ptr, len, copy, TRUE);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001001 i = fwrite(copy, len, (size_t)1, bi->bi_fp);
1002 if (copy != small_buf)
1003 vim_free(copy);
1004 return i == 1 ? OK : FAIL;
Bram Moolenaar191e0a22010-06-14 01:39:13 +02001005 }
Bram Moolenaar191e0a22010-06-14 01:39:13 +02001006#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001007 return undo_write(bi, ptr, len);
Bram Moolenaar191e0a22010-06-14 01:39:13 +02001008}
1009
1010/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001011 * Write a number, MSB first, in "len" bytes.
1012 * Must match with undo_read_?c() functions.
1013 * Returns OK or FAIL.
Bram Moolenaar191e0a22010-06-14 01:39:13 +02001014 */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001015 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001016undo_write_bytes(bufinfo_T *bi, long_u nr, int len)
Bram Moolenaar191e0a22010-06-14 01:39:13 +02001017{
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001018 char_u buf[8];
1019 int i;
1020 int bufi = 0;
Bram Moolenaar191e0a22010-06-14 01:39:13 +02001021
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001022 for (i = len - 1; i >= 0; --i)
Bram Moolenaar9b8f0212014-08-13 22:05:53 +02001023 buf[bufi++] = (char_u)(nr >> (i * 8));
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001024 return undo_write(bi, buf, (size_t)len);
1025}
1026
1027/*
1028 * Write the pointer to an undo header. Instead of writing the pointer itself
1029 * we use the sequence number of the header. This is converted back to
1030 * pointers when reading. */
1031 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001032put_header_ptr(bufinfo_T *bi, u_header_T *uhp)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001033{
1034 undo_write_bytes(bi, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
Bram Moolenaar191e0a22010-06-14 01:39:13 +02001035}
1036
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001037 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001038undo_read_4c(bufinfo_T *bi)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001039{
1040#ifdef FEAT_CRYPT
1041 if (bi->bi_buffer != NULL)
1042 {
1043 char_u buf[4];
1044 int n;
1045
1046 undo_read(bi, buf, (size_t)4);
Bram Moolenaar35169282014-09-11 22:50:09 +02001047 n = ((unsigned)buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3];
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001048 return n;
1049 }
1050#endif
1051 return get4c(bi->bi_fp);
1052}
1053
1054 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001055undo_read_2c(bufinfo_T *bi)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001056{
1057#ifdef FEAT_CRYPT
1058 if (bi->bi_buffer != NULL)
1059 {
1060 char_u buf[2];
1061 int n;
1062
1063 undo_read(bi, buf, (size_t)2);
1064 n = (buf[0] << 8) + buf[1];
1065 return n;
1066 }
1067#endif
1068 return get2c(bi->bi_fp);
1069}
1070
1071 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001072undo_read_byte(bufinfo_T *bi)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001073{
1074#ifdef FEAT_CRYPT
1075 if (bi->bi_buffer != NULL)
1076 {
1077 char_u buf[1];
1078
1079 undo_read(bi, buf, (size_t)1);
1080 return buf[0];
1081 }
1082#endif
1083 return getc(bi->bi_fp);
1084}
1085
1086 static time_t
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001087undo_read_time(bufinfo_T *bi)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001088{
1089#ifdef FEAT_CRYPT
1090 if (bi->bi_buffer != NULL)
1091 {
1092 char_u buf[8];
1093 time_t n = 0;
1094 int i;
1095
1096 undo_read(bi, buf, (size_t)8);
1097 for (i = 0; i < 8; ++i)
1098 n = (n << 8) + buf[i];
1099 return n;
1100 }
1101#endif
1102 return get8ctime(bi->bi_fp);
1103}
1104
1105/*
1106 * Read "buffer[size]" from the undo file.
1107 * Return OK or FAIL.
1108 */
1109 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001110undo_read(bufinfo_T *bi, char_u *buffer, size_t size)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001111{
Bram Moolenaar56f2db52017-06-11 23:09:15 +02001112 int retval = OK;
1113
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001114#ifdef FEAT_CRYPT
1115 if (bi->bi_buffer != NULL)
1116 {
Bram Moolenaar9b8f0212014-08-13 22:05:53 +02001117 int size_todo = (int)size;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001118 char_u *p = buffer;
1119
1120 while (size_todo > 0)
1121 {
1122 size_t n;
1123
1124 if (bi->bi_used >= bi->bi_avail)
1125 {
1126 n = fread(bi->bi_buffer, 1, (size_t)CRYPT_BUF_SIZE, bi->bi_fp);
Bram Moolenaar55952d42016-11-05 14:58:34 +01001127 if (n == 0)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001128 {
Bram Moolenaar56f2db52017-06-11 23:09:15 +02001129 retval = FAIL;
1130 break;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001131 }
1132 bi->bi_avail = n;
1133 bi->bi_used = 0;
Christian Brabandtf573c6e2021-06-20 14:02:16 +02001134 crypt_decode_inplace(bi->bi_state, bi->bi_buffer, bi->bi_avail, FALSE);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001135 }
1136 n = size_todo;
1137 if (n > bi->bi_avail - bi->bi_used)
1138 n = bi->bi_avail - bi->bi_used;
1139 mch_memmove(p, bi->bi_buffer + bi->bi_used, n);
1140 bi->bi_used += n;
Bram Moolenaar9b8f0212014-08-13 22:05:53 +02001141 size_todo -= (int)n;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001142 p += n;
1143 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001144 }
Bram Moolenaar56f2db52017-06-11 23:09:15 +02001145 else
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001146#endif
1147 if (fread(buffer, (size_t)size, 1, bi->bi_fp) != 1)
Bram Moolenaar56f2db52017-06-11 23:09:15 +02001148 retval = FAIL;
1149
1150 if (retval == FAIL)
Bram Moolenaare38eab22019-12-05 21:50:01 +01001151 // Error may be checked for only later. Fill with zeros,
1152 // so that the reader won't use garbage.
Bram Moolenaar56f2db52017-06-11 23:09:15 +02001153 vim_memset(buffer, 0, size);
1154 return retval;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001155}
1156
1157/*
1158 * Read a string of length "len" from "bi->bi_fd".
1159 * "len" can be zero to allocate an empty line.
1160 * Decrypt the bytes if needed.
1161 * Append a NUL.
1162 * Returns a pointer to allocated memory or NULL for failure.
1163 */
1164 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001165read_string_decrypt(bufinfo_T *bi, int len)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001166{
Bram Moolenaar964b3742019-05-24 18:54:09 +02001167 char_u *ptr = alloc(len + 1);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001168
1169 if (ptr != NULL)
1170 {
1171 if (len > 0 && undo_read(bi, ptr, len) == FAIL)
1172 {
1173 vim_free(ptr);
1174 return NULL;
1175 }
Bram Moolenaarccae4672019-01-04 15:09:57 +01001176 // In case there are text properties there already is a NUL, but
1177 // checking for that is more expensive than just adding a dummy byte.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001178 ptr[len] = NUL;
1179#ifdef FEAT_CRYPT
1180 if (bi->bi_state != NULL && bi->bi_buffer == NULL)
Christian Brabandtf573c6e2021-06-20 14:02:16 +02001181 crypt_decode_inplace(bi->bi_state, ptr, len, FALSE);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001182#endif
1183 }
1184 return ptr;
1185}
1186
1187/*
1188 * Writes the (not encrypted) header and initializes encryption if needed.
1189 */
1190 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001191serialize_header(bufinfo_T *bi, char_u *hash)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001192{
Bram Moolenaarccae4672019-01-04 15:09:57 +01001193 long len;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001194 buf_T *buf = bi->bi_buf;
1195 FILE *fp = bi->bi_fp;
1196 char_u time_buf[8];
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001197
Bram Moolenaare38eab22019-12-05 21:50:01 +01001198 // Start writing, first the magic marker and undo info version.
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001199 if (fwrite(UF_START_MAGIC, (size_t)UF_START_MAGIC_LEN, (size_t)1, fp) != 1)
1200 return FAIL;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001201
Bram Moolenaare38eab22019-12-05 21:50:01 +01001202 // If the buffer is encrypted then all text bytes following will be
1203 // encrypted. Numbers and other info is not crypted.
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001204#ifdef FEAT_CRYPT
Bram Moolenaarfa0ff9a2010-07-25 16:05:19 +02001205 if (*buf->b_p_key != NUL)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001206 {
1207 char_u *header;
1208 int header_len;
1209
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001210 undo_write_bytes(bi, (long_u)UF_VERSION_CRYPT, 2);
1211 bi->bi_state = crypt_create_for_writing(crypt_get_method_nr(buf),
1212 buf->b_p_key, &header, &header_len);
1213 if (bi->bi_state == NULL)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001214 return FAIL;
Bram Moolenaarccae4672019-01-04 15:09:57 +01001215 len = (long)fwrite(header, (size_t)header_len, (size_t)1, fp);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001216 vim_free(header);
1217 if (len != 1)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001218 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001219 crypt_free_state(bi->bi_state);
1220 bi->bi_state = NULL;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001221 return FAIL;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001222 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001223
1224 if (crypt_whole_undofile(crypt_get_method_nr(buf)))
1225 {
1226 bi->bi_buffer = alloc(CRYPT_BUF_SIZE);
1227 if (bi->bi_buffer == NULL)
1228 {
1229 crypt_free_state(bi->bi_state);
1230 bi->bi_state = NULL;
1231 return FAIL;
1232 }
1233 bi->bi_used = 0;
1234 }
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001235 }
1236 else
1237#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001238 undo_write_bytes(bi, (long_u)UF_VERSION, 2);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001239
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001240
Bram Moolenaare38eab22019-12-05 21:50:01 +01001241 // Write a hash of the buffer text, so that we can verify it is still the
1242 // same when reading the buffer text.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001243 if (undo_write(bi, hash, (size_t)UNDO_HASH_SIZE) == FAIL)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001244 return FAIL;
1245
Bram Moolenaare38eab22019-12-05 21:50:01 +01001246 // buffer-specific data
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001247 undo_write_bytes(bi, (long_u)buf->b_ml.ml_line_count, 4);
Bram Moolenaarccae4672019-01-04 15:09:57 +01001248 len = buf->b_u_line_ptr.ul_line == NULL
Bram Moolenaar8aef43b2019-01-08 20:14:35 +01001249 ? 0L : (long)STRLEN(buf->b_u_line_ptr.ul_line);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001250 undo_write_bytes(bi, (long_u)len, 4);
Bram Moolenaar8aef43b2019-01-08 20:14:35 +01001251 if (len > 0 && fwrite_crypt(bi, buf->b_u_line_ptr.ul_line, (size_t)len)
1252 == FAIL)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001253 return FAIL;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001254 undo_write_bytes(bi, (long_u)buf->b_u_line_lnum, 4);
1255 undo_write_bytes(bi, (long_u)buf->b_u_line_colnr, 4);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001256
Bram Moolenaare38eab22019-12-05 21:50:01 +01001257 // Undo structures header data
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001258 put_header_ptr(bi, buf->b_u_oldhead);
1259 put_header_ptr(bi, buf->b_u_newhead);
1260 put_header_ptr(bi, buf->b_u_curhead);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001261
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001262 undo_write_bytes(bi, (long_u)buf->b_u_numhead, 4);
1263 undo_write_bytes(bi, (long_u)buf->b_u_seq_last, 4);
1264 undo_write_bytes(bi, (long_u)buf->b_u_seq_cur, 4);
1265 time_to_bytes(buf->b_u_time_cur, time_buf);
1266 undo_write(bi, time_buf, 8);
Bram Moolenaara800b422010-06-27 01:15:55 +02001267
Bram Moolenaare38eab22019-12-05 21:50:01 +01001268 // Optional fields.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001269 undo_write_bytes(bi, 4, 1);
1270 undo_write_bytes(bi, UF_LAST_SAVE_NR, 1);
1271 undo_write_bytes(bi, (long_u)buf->b_u_save_nr_last, 4);
Bram Moolenaara800b422010-06-27 01:15:55 +02001272
Bram Moolenaare38eab22019-12-05 21:50:01 +01001273 undo_write_bytes(bi, 0, 1); // end marker
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001274
1275 return OK;
1276}
1277
1278 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001279serialize_uhp(bufinfo_T *bi, u_header_T *uhp)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001280{
1281 int i;
1282 u_entry_T *uep;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001283 char_u time_buf[8];
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001284
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001285 if (undo_write_bytes(bi, (long_u)UF_HEADER_MAGIC, 2) == FAIL)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001286 return FAIL;
1287
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001288 put_header_ptr(bi, uhp->uh_next.ptr);
1289 put_header_ptr(bi, uhp->uh_prev.ptr);
1290 put_header_ptr(bi, uhp->uh_alt_next.ptr);
1291 put_header_ptr(bi, uhp->uh_alt_prev.ptr);
1292 undo_write_bytes(bi, uhp->uh_seq, 4);
1293 serialize_pos(bi, uhp->uh_cursor);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001294 undo_write_bytes(bi, (long_u)uhp->uh_cursor_vcol, 4);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001295 undo_write_bytes(bi, (long_u)uhp->uh_flags, 2);
Bram Moolenaare38eab22019-12-05 21:50:01 +01001296 // Assume NMARKS will stay the same.
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001297 for (i = 0; i < NMARKS; ++i)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001298 serialize_pos(bi, uhp->uh_namedm[i]);
1299 serialize_visualinfo(bi, &uhp->uh_visual);
1300 time_to_bytes(uhp->uh_time, time_buf);
1301 undo_write(bi, time_buf, 8);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001302
Bram Moolenaare38eab22019-12-05 21:50:01 +01001303 // Optional fields.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001304 undo_write_bytes(bi, 4, 1);
1305 undo_write_bytes(bi, UHP_SAVE_NR, 1);
1306 undo_write_bytes(bi, (long_u)uhp->uh_save_nr, 4);
Bram Moolenaara800b422010-06-27 01:15:55 +02001307
Bram Moolenaare38eab22019-12-05 21:50:01 +01001308 undo_write_bytes(bi, 0, 1); // end marker
Bram Moolenaara800b422010-06-27 01:15:55 +02001309
Bram Moolenaare38eab22019-12-05 21:50:01 +01001310 // Write all the entries.
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001311 for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
1312 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001313 undo_write_bytes(bi, (long_u)UF_ENTRY_MAGIC, 2);
1314 if (serialize_uep(bi, uep) == FAIL)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001315 return FAIL;
1316 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001317 undo_write_bytes(bi, (long_u)UF_ENTRY_END_MAGIC, 2);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001318 return OK;
1319}
1320
1321 static u_header_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001322unserialize_uhp(bufinfo_T *bi, char_u *file_name)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001323{
1324 u_header_T *uhp;
1325 int i;
1326 u_entry_T *uep, *last_uep;
1327 int c;
1328 int error;
1329
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001330 uhp = U_ALLOC_LINE(sizeof(u_header_T));
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001331 if (uhp == NULL)
1332 return NULL;
Bram Moolenaara80faa82020-04-12 19:37:17 +02001333 CLEAR_POINTER(uhp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001334#ifdef U_DEBUG
1335 uhp->uh_magic = UH_MAGIC;
1336#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001337 uhp->uh_next.seq = undo_read_4c(bi);
1338 uhp->uh_prev.seq = undo_read_4c(bi);
1339 uhp->uh_alt_next.seq = undo_read_4c(bi);
1340 uhp->uh_alt_prev.seq = undo_read_4c(bi);
1341 uhp->uh_seq = undo_read_4c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001342 if (uhp->uh_seq <= 0)
1343 {
1344 corruption_error("uh_seq", file_name);
1345 vim_free(uhp);
1346 return NULL;
1347 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001348 unserialize_pos(bi, &uhp->uh_cursor);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001349 uhp->uh_cursor_vcol = undo_read_4c(bi);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001350 uhp->uh_flags = undo_read_2c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001351 for (i = 0; i < NMARKS; ++i)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001352 unserialize_pos(bi, &uhp->uh_namedm[i]);
1353 unserialize_visualinfo(bi, &uhp->uh_visual);
1354 uhp->uh_time = undo_read_time(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001355
Bram Moolenaare38eab22019-12-05 21:50:01 +01001356 // Optional fields.
Bram Moolenaar69154f22010-07-18 21:42:34 +02001357 for (;;)
1358 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001359 int len = undo_read_byte(bi);
Bram Moolenaar69154f22010-07-18 21:42:34 +02001360 int what;
Bram Moolenaara800b422010-06-27 01:15:55 +02001361
Bram Moolenaarfb06d762019-08-04 18:55:35 +02001362 if (len == EOF)
1363 {
1364 corruption_error("truncated", file_name);
1365 u_free_uhp(uhp);
1366 return NULL;
1367 }
Bram Moolenaar69154f22010-07-18 21:42:34 +02001368 if (len == 0)
1369 break;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001370 what = undo_read_byte(bi);
Bram Moolenaar69154f22010-07-18 21:42:34 +02001371 switch (what)
1372 {
1373 case UHP_SAVE_NR:
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001374 uhp->uh_save_nr = undo_read_4c(bi);
Bram Moolenaara800b422010-06-27 01:15:55 +02001375 break;
Bram Moolenaar69154f22010-07-18 21:42:34 +02001376 default:
Bram Moolenaare38eab22019-12-05 21:50:01 +01001377 // field not supported, skip
Bram Moolenaar69154f22010-07-18 21:42:34 +02001378 while (--len >= 0)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001379 (void)undo_read_byte(bi);
Bram Moolenaara800b422010-06-27 01:15:55 +02001380 }
Bram Moolenaar69154f22010-07-18 21:42:34 +02001381 }
Bram Moolenaara800b422010-06-27 01:15:55 +02001382
Bram Moolenaare38eab22019-12-05 21:50:01 +01001383 // Unserialize the uep list.
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001384 last_uep = NULL;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001385 while ((c = undo_read_2c(bi)) == UF_ENTRY_MAGIC)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001386 {
1387 error = FALSE;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001388 uep = unserialize_uep(bi, &error, file_name);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001389 if (last_uep == NULL)
1390 uhp->uh_entry = uep;
1391 else
1392 last_uep->ue_next = uep;
1393 last_uep = uep;
1394 if (uep == NULL || error)
1395 {
1396 u_free_uhp(uhp);
1397 return NULL;
1398 }
1399 }
1400 if (c != UF_ENTRY_END_MAGIC)
1401 {
1402 corruption_error("entry end", file_name);
1403 u_free_uhp(uhp);
1404 return NULL;
1405 }
1406
1407 return uhp;
1408}
1409
Bram Moolenaar9db58062010-05-29 20:33:07 +02001410/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001411 * Serialize "uep".
Bram Moolenaar9db58062010-05-29 20:33:07 +02001412 */
1413 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001414serialize_uep(
1415 bufinfo_T *bi,
1416 u_entry_T *uep)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001417{
1418 int i;
1419 size_t len;
1420
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001421 undo_write_bytes(bi, (long_u)uep->ue_top, 4);
1422 undo_write_bytes(bi, (long_u)uep->ue_bot, 4);
1423 undo_write_bytes(bi, (long_u)uep->ue_lcount, 4);
1424 undo_write_bytes(bi, (long_u)uep->ue_size, 4);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001425 for (i = 0; i < uep->ue_size; ++i)
1426 {
Bram Moolenaarccae4672019-01-04 15:09:57 +01001427 // Text is written without the text properties, since we cannot restore
1428 // the text property types.
1429 len = STRLEN(uep->ue_array[i].ul_line);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001430 if (undo_write_bytes(bi, (long_u)len, 4) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001431 return FAIL;
Bram Moolenaarccae4672019-01-04 15:09:57 +01001432 if (len > 0 && fwrite_crypt(bi, uep->ue_array[i].ul_line, len) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001433 return FAIL;
1434 }
1435 return OK;
1436}
1437
1438 static u_entry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001439unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001440{
1441 int i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001442 u_entry_T *uep;
Bram Moolenaarccae4672019-01-04 15:09:57 +01001443 undoline_T *array = NULL;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001444 char_u *line;
1445 int line_len;
1446
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001447 uep = U_ALLOC_LINE(sizeof(u_entry_T));
Bram Moolenaar9db58062010-05-29 20:33:07 +02001448 if (uep == NULL)
1449 return NULL;
Bram Moolenaara80faa82020-04-12 19:37:17 +02001450 CLEAR_POINTER(uep);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001451#ifdef U_DEBUG
1452 uep->ue_magic = UE_MAGIC;
1453#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001454 uep->ue_top = undo_read_4c(bi);
1455 uep->ue_bot = undo_read_4c(bi);
1456 uep->ue_lcount = undo_read_4c(bi);
1457 uep->ue_size = undo_read_4c(bi);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001458 if (uep->ue_size > 0)
1459 {
Bram Moolenaar0c8485f2017-02-26 18:17:10 +01001460 if (uep->ue_size < LONG_MAX / (int)sizeof(char_u *))
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001461 array = U_ALLOC_LINE(sizeof(undoline_T) * uep->ue_size);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001462 if (array == NULL)
1463 {
1464 *error = TRUE;
1465 return uep;
1466 }
Bram Moolenaarccae4672019-01-04 15:09:57 +01001467 vim_memset(array, 0, sizeof(undoline_T) * uep->ue_size);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001468 }
1469 uep->ue_array = array;
1470
1471 for (i = 0; i < uep->ue_size; ++i)
1472 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001473 line_len = undo_read_4c(bi);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001474 if (line_len >= 0)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001475 line = read_string_decrypt(bi, line_len);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001476 else
1477 {
1478 line = NULL;
1479 corruption_error("line length", file_name);
1480 }
1481 if (line == NULL)
1482 {
1483 *error = TRUE;
1484 return uep;
1485 }
Bram Moolenaarccae4672019-01-04 15:09:57 +01001486 array[i].ul_line = line;
1487 array[i].ul_len = line_len + 1;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001488 }
1489 return uep;
1490}
1491
1492/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001493 * Serialize "pos".
Bram Moolenaar9db58062010-05-29 20:33:07 +02001494 */
1495 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001496serialize_pos(bufinfo_T *bi, pos_T pos)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001497{
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001498 undo_write_bytes(bi, (long_u)pos.lnum, 4);
1499 undo_write_bytes(bi, (long_u)pos.col, 4);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001500 undo_write_bytes(bi, (long_u)pos.coladd, 4);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001501}
1502
1503/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001504 * Unserialize the pos_T at the current position.
Bram Moolenaar9db58062010-05-29 20:33:07 +02001505 */
1506 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001507unserialize_pos(bufinfo_T *bi, pos_T *pos)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001508{
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001509 pos->lnum = undo_read_4c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001510 if (pos->lnum < 0)
1511 pos->lnum = 0;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001512 pos->col = undo_read_4c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001513 if (pos->col < 0)
1514 pos->col = 0;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001515 pos->coladd = undo_read_4c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001516 if (pos->coladd < 0)
1517 pos->coladd = 0;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001518}
1519
1520/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001521 * Serialize "info".
Bram Moolenaar9db58062010-05-29 20:33:07 +02001522 */
1523 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001524serialize_visualinfo(bufinfo_T *bi, visualinfo_T *info)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001525{
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001526 serialize_pos(bi, info->vi_start);
1527 serialize_pos(bi, info->vi_end);
1528 undo_write_bytes(bi, (long_u)info->vi_mode, 4);
1529 undo_write_bytes(bi, (long_u)info->vi_curswant, 4);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001530}
1531
1532/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001533 * Unserialize the visualinfo_T at the current position.
Bram Moolenaar9db58062010-05-29 20:33:07 +02001534 */
1535 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001536unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001537{
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001538 unserialize_pos(bi, &info->vi_start);
1539 unserialize_pos(bi, &info->vi_end);
1540 info->vi_mode = undo_read_4c(bi);
1541 info->vi_curswant = undo_read_4c(bi);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001542}
1543
1544/*
1545 * Write the undo tree in an undo file.
1546 * When "name" is not NULL, use it as the name of the undo file.
1547 * Otherwise use buf->b_ffname to generate the undo file name.
1548 * "buf" must never be null, buf->b_ffname is used to obtain the original file
1549 * permissions.
1550 * "forceit" is TRUE for ":wundo!", FALSE otherwise.
1551 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
1552 */
1553 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001554u_write_undo(
1555 char_u *name,
1556 int forceit,
1557 buf_T *buf,
1558 char_u *hash)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001559{
1560 u_header_T *uhp;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001561 char_u *file_name;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001562 int mark;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001563#ifdef U_DEBUG
1564 int headers_written = 0;
1565#endif
1566 int fd;
1567 FILE *fp = NULL;
1568 int perm;
1569 int write_ok = FALSE;
1570#ifdef UNIX
1571 int st_old_valid = FALSE;
Bram Moolenaar8767f522016-07-01 17:17:39 +02001572 stat_T st_old;
1573 stat_T st_new;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001574#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001575 bufinfo_T bi;
1576
Bram Moolenaara80faa82020-04-12 19:37:17 +02001577 CLEAR_FIELD(bi);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001578
1579 if (name == NULL)
1580 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001581 file_name = u_get_undo_file_name(buf->b_ffname, FALSE);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001582 if (file_name == NULL)
1583 {
1584 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001585 {
1586 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001587 smsg(
Bram Moolenaar504a8212010-05-30 17:17:42 +02001588 _("Cannot write undo file in any directory in 'undodir'"));
1589 verbose_leave();
1590 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001591 return;
1592 }
1593 }
1594 else
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001595 file_name = name;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001596
1597 /*
1598 * Decide about the permission to use for the undo file. If the buffer
1599 * has a name use the permission of the original file. Otherwise only
1600 * allow the user to access the undo file.
1601 */
1602 perm = 0600;
1603 if (buf->b_ffname != NULL)
1604 {
1605#ifdef UNIX
1606 if (mch_stat((char *)buf->b_ffname, &st_old) >= 0)
1607 {
1608 perm = st_old.st_mode;
1609 st_old_valid = TRUE;
1610 }
1611#else
1612 perm = mch_getperm(buf->b_ffname);
1613 if (perm < 0)
1614 perm = 0600;
1615#endif
1616 }
1617
Bram Moolenaare38eab22019-12-05 21:50:01 +01001618 // strip any s-bit and executable bit
Bram Moolenaar3cbac302015-04-21 16:12:06 +02001619 perm = perm & 0666;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001620
Bram Moolenaare38eab22019-12-05 21:50:01 +01001621 // If the undo file already exists, verify that it actually is an undo
1622 // file, and delete it.
Bram Moolenaar9db58062010-05-29 20:33:07 +02001623 if (mch_getperm(file_name) >= 0)
1624 {
1625 if (name == NULL || !forceit)
1626 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01001627 // Check we can read it and it's an undo file.
Bram Moolenaar9db58062010-05-29 20:33:07 +02001628 fd = mch_open((char *)file_name, O_RDONLY|O_EXTRA, 0);
1629 if (fd < 0)
1630 {
1631 if (name != NULL || p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001632 {
1633 if (name == NULL)
1634 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001635 smsg(
Bram Moolenaar504a8212010-05-30 17:17:42 +02001636 _("Will not overwrite with undo file, cannot read: %s"),
Bram Moolenaar9db58062010-05-29 20:33:07 +02001637 file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001638 if (name == NULL)
1639 verbose_leave();
1640 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001641 goto theend;
1642 }
1643 else
1644 {
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001645 char_u mbuf[UF_START_MAGIC_LEN];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001646 int len;
1647
Bram Moolenaar540fc6f2010-12-17 16:27:16 +01001648 len = read_eintr(fd, mbuf, UF_START_MAGIC_LEN);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001649 close(fd);
1650 if (len < UF_START_MAGIC_LEN
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001651 || memcmp(mbuf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001652 {
1653 if (name != NULL || p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001654 {
1655 if (name == NULL)
1656 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001657 smsg(
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001658 _("Will not overwrite, this is not an undo file: %s"),
Bram Moolenaar9db58062010-05-29 20:33:07 +02001659 file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001660 if (name == NULL)
1661 verbose_leave();
1662 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001663 goto theend;
1664 }
1665 }
1666 }
1667 mch_remove(file_name);
1668 }
1669
Bram Moolenaare38eab22019-12-05 21:50:01 +01001670 // If there is no undo information at all, quit here after deleting any
1671 // existing undo file.
Bram Moolenaarccae4672019-01-04 15:09:57 +01001672 if (buf->b_u_numhead == 0 && buf->b_u_line_ptr.ul_line == NULL)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001673 {
1674 if (p_verbose > 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001675 verb_msg(_("Skipping undo file write, nothing to undo"));
Bram Moolenaar504a8212010-05-30 17:17:42 +02001676 goto theend;
1677 }
1678
Bram Moolenaar9db58062010-05-29 20:33:07 +02001679 fd = mch_open((char *)file_name,
1680 O_CREAT|O_EXTRA|O_WRONLY|O_EXCL|O_NOFOLLOW, perm);
1681 if (fd < 0)
1682 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001683 semsg(_(e_not_open), file_name);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001684 goto theend;
1685 }
1686 (void)mch_setperm(file_name, perm);
1687 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001688 {
1689 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001690 smsg(_("Writing undo file: %s"), file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001691 verbose_leave();
1692 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001693
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001694#ifdef U_DEBUG
Bram Moolenaare38eab22019-12-05 21:50:01 +01001695 // Check there is no problem in undo info before writing.
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001696 u_check(FALSE);
1697#endif
1698
Bram Moolenaar9db58062010-05-29 20:33:07 +02001699#ifdef UNIX
1700 /*
1701 * Try to set the group of the undo file same as the original file. If
1702 * this fails, set the protection bits for the group same as the
1703 * protection bits for others.
1704 */
Bram Moolenaarce69e822010-07-21 20:31:07 +02001705 if (st_old_valid
1706 && mch_stat((char *)file_name, &st_new) >= 0
1707 && st_new.st_gid != st_old.st_gid
Bram Moolenaare38eab22019-12-05 21:50:01 +01001708# ifdef HAVE_FCHOWN // sequent-ptx lacks fchown()
Bram Moolenaarce69e822010-07-21 20:31:07 +02001709 && fchown(fd, (uid_t)-1, st_old.st_gid) != 0
Bram Moolenaar9db58062010-05-29 20:33:07 +02001710# endif
1711 )
1712 mch_setperm(file_name, (perm & 0707) | ((perm & 07) << 3));
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02001713# if defined(HAVE_SELINUX) || defined(HAVE_SMACK)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001714 if (buf->b_ffname != NULL)
1715 mch_copy_sec(buf->b_ffname, file_name);
1716# endif
1717#endif
1718
1719 fp = fdopen(fd, "w");
1720 if (fp == NULL)
1721 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001722 semsg(_(e_not_open), file_name);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001723 close(fd);
1724 mch_remove(file_name);
1725 goto theend;
1726 }
1727
Bram Moolenaare38eab22019-12-05 21:50:01 +01001728 // Undo must be synced.
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001729 u_sync(TRUE);
1730
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001731 /*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001732 * Write the header. Initializes encryption, if enabled.
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001733 */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001734 bi.bi_buf = buf;
1735 bi.bi_fp = fp;
1736 if (serialize_header(&bi, hash) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001737 goto write_error;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001738
1739 /*
1740 * Iteratively serialize UHPs and their UEPs from the top down.
1741 */
1742 mark = ++lastmark;
1743 uhp = buf->b_u_oldhead;
1744 while (uhp != NULL)
1745 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01001746 // Serialize current UHP if we haven't seen it
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001747 if (uhp->uh_walk != mark)
1748 {
1749 uhp->uh_walk = mark;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001750#ifdef U_DEBUG
1751 ++headers_written;
1752#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001753 if (serialize_uhp(&bi, uhp) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001754 goto write_error;
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001755 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001756
Bram Moolenaare38eab22019-12-05 21:50:01 +01001757 // Now walk through the tree - algorithm from undo_time().
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001758 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != mark)
1759 uhp = uhp->uh_prev.ptr;
1760 else if (uhp->uh_alt_next.ptr != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001761 && uhp->uh_alt_next.ptr->uh_walk != mark)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001762 uhp = uhp->uh_alt_next.ptr;
1763 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001764 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001765 uhp = uhp->uh_next.ptr;
1766 else if (uhp->uh_alt_prev.ptr != NULL)
1767 uhp = uhp->uh_alt_prev.ptr;
1768 else
1769 uhp = uhp->uh_next.ptr;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001770 }
1771
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001772 if (undo_write_bytes(&bi, (long_u)UF_HEADER_END_MAGIC, 2) == OK)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001773 write_ok = TRUE;
1774#ifdef U_DEBUG
1775 if (headers_written != buf->b_u_numhead)
Bram Moolenaar570064c2013-06-10 20:25:10 +02001776 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001777 semsg("Written %ld headers, ...", headers_written);
1778 semsg("... but numhead is %ld", buf->b_u_numhead);
Bram Moolenaar570064c2013-06-10 20:25:10 +02001779 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001780#endif
1781
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001782#ifdef FEAT_CRYPT
1783 if (bi.bi_state != NULL && undo_flush(&bi) == FAIL)
1784 write_ok = FALSE;
1785#endif
1786
Bram Moolenaar340dd0f2021-10-14 17:52:23 +01001787#if defined(UNIX) && defined(HAVE_FSYNC)
1788 if (p_fs && fflush(fp) == 0 && vim_fsync(fd) != 0)
1789 write_ok = FALSE;
1790#endif
1791
Bram Moolenaar9db58062010-05-29 20:33:07 +02001792write_error:
1793 fclose(fp);
1794 if (!write_ok)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001795 semsg(_("E829: write error in undo file: %s"), file_name);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001796
Bram Moolenaar4f974752019-02-17 17:44:42 +01001797#if defined(MSWIN)
Bram Moolenaare38eab22019-12-05 21:50:01 +01001798 // Copy file attributes; for systems where this can only be done after
1799 // closing the file.
Bram Moolenaar9db58062010-05-29 20:33:07 +02001800 if (buf->b_ffname != NULL)
1801 (void)mch_copy_file_attribute(buf->b_ffname, file_name);
1802#endif
1803#ifdef HAVE_ACL
1804 if (buf->b_ffname != NULL)
1805 {
1806 vim_acl_T acl;
1807
Bram Moolenaare38eab22019-12-05 21:50:01 +01001808 // For systems that support ACL: get the ACL from the original file.
Bram Moolenaar9db58062010-05-29 20:33:07 +02001809 acl = mch_get_acl(buf->b_ffname);
1810 mch_set_acl(file_name, acl);
Bram Moolenaard2aed442012-06-01 13:46:12 +02001811 mch_free_acl(acl);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001812 }
1813#endif
1814
1815theend:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001816#ifdef FEAT_CRYPT
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001817 if (bi.bi_state != NULL)
1818 crypt_free_state(bi.bi_state);
1819 vim_free(bi.bi_buffer);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001820#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02001821 if (file_name != name)
1822 vim_free(file_name);
1823}
1824
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001825/*
1826 * Load the undo tree from an undo file.
1827 * If "name" is not NULL use it as the undo file name. This also means being
1828 * a bit more verbose.
1829 * Otherwise use curbuf->b_ffname to generate the undo file name.
1830 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
1831 */
1832 void
Bram Moolenaarbd67aac2019-09-21 23:09:04 +02001833u_read_undo(char_u *name, char_u *hash, char_u *orig_name UNUSED)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001834{
1835 char_u *file_name;
1836 FILE *fp;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001837 long version, str_len;
Bram Moolenaarccae4672019-01-04 15:09:57 +01001838 undoline_T line_ptr;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001839 linenr_T line_lnum;
1840 colnr_T line_colnr;
1841 linenr_T line_count;
Bram Moolenaar3eb16372017-02-26 18:11:36 +01001842 long num_head = 0;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001843 long old_header_seq, new_header_seq, cur_header_seq;
1844 long seq_last, seq_cur;
Bram Moolenaarb2c03502010-07-02 20:20:09 +02001845 long last_save_nr = 0;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001846 short old_idx = -1, new_idx = -1, cur_idx = -1;
1847 long num_read_uhps = 0;
1848 time_t seq_time;
1849 int i, j;
1850 int c;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001851 u_header_T *uhp;
1852 u_header_T **uhp_table = NULL;
1853 char_u read_hash[UNDO_HASH_SIZE];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001854 char_u magic_buf[UF_START_MAGIC_LEN];
1855#ifdef U_DEBUG
1856 int *uhp_table_used;
1857#endif
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001858#ifdef UNIX
Bram Moolenaar8767f522016-07-01 17:17:39 +02001859 stat_T st_orig;
1860 stat_T st_undo;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001861#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001862 bufinfo_T bi;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001863
Bram Moolenaara80faa82020-04-12 19:37:17 +02001864 CLEAR_FIELD(bi);
Bram Moolenaarccae4672019-01-04 15:09:57 +01001865 line_ptr.ul_len = 0;
1866 line_ptr.ul_line = NULL;
1867
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001868 if (name == NULL)
1869 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001870 file_name = u_get_undo_file_name(curbuf->b_ffname, TRUE);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001871 if (file_name == NULL)
1872 return;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001873
1874#ifdef UNIX
Bram Moolenaare38eab22019-12-05 21:50:01 +01001875 // For safety we only read an undo file if the owner is equal to the
1876 // owner of the text file or equal to the current user.
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001877 if (mch_stat((char *)orig_name, &st_orig) >= 0
1878 && mch_stat((char *)file_name, &st_undo) >= 0
Bram Moolenaar3b262392013-09-08 15:40:49 +02001879 && st_orig.st_uid != st_undo.st_uid
1880 && st_undo.st_uid != getuid())
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001881 {
1882 if (p_verbose > 0)
1883 {
1884 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001885 smsg(_("Not reading undo file, owner differs: %s"),
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001886 file_name);
1887 verbose_leave();
1888 }
1889 return;
1890 }
1891#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001892 }
1893 else
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001894 file_name = name;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001895
1896 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001897 {
1898 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001899 smsg(_("Reading undo file: %s"), file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001900 verbose_leave();
1901 }
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001902
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001903 fp = mch_fopen((char *)file_name, "r");
1904 if (fp == NULL)
1905 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001906 if (name != NULL || p_verbose > 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001907 semsg(_("E822: Cannot open undo file for reading: %s"), file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001908 goto error;
1909 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001910 bi.bi_buf = curbuf;
1911 bi.bi_fp = fp;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001912
Bram Moolenaar9db58062010-05-29 20:33:07 +02001913 /*
1914 * Read the undo file header.
1915 */
1916 if (fread(magic_buf, UF_START_MAGIC_LEN, 1, fp) != 1
1917 || memcmp(magic_buf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001918 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001919 semsg(_("E823: Not an undo file: %s"), file_name);
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001920 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001921 }
1922 version = get2c(fp);
Bram Moolenaar69154f22010-07-18 21:42:34 +02001923 if (version == UF_VERSION_CRYPT)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001924 {
1925#ifdef FEAT_CRYPT
Bram Moolenaar56be9502010-06-06 14:20:26 +02001926 if (*curbuf->b_p_key == NUL)
1927 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001928 semsg(_("E832: Non-encrypted file has encrypted undo file: %s"),
Bram Moolenaar56be9502010-06-06 14:20:26 +02001929 file_name);
1930 goto error;
1931 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001932 bi.bi_state = crypt_create_from_file(fp, curbuf->b_p_key);
1933 if (bi.bi_state == NULL)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001934 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001935 semsg(_("E826: Undo file decryption failed: %s"), file_name);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001936 goto error;
1937 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001938 if (crypt_whole_undofile(bi.bi_state->method_nr))
1939 {
1940 bi.bi_buffer = alloc(CRYPT_BUF_SIZE);
1941 if (bi.bi_buffer == NULL)
1942 {
1943 crypt_free_state(bi.bi_state);
1944 bi.bi_state = NULL;
1945 goto error;
1946 }
1947 bi.bi_avail = 0;
1948 bi.bi_used = 0;
1949 }
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001950#else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001951 semsg(_("E827: Undo file is encrypted: %s"), file_name);
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001952 goto error;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001953#endif
1954 }
Bram Moolenaar69154f22010-07-18 21:42:34 +02001955 else if (version != UF_VERSION)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001956 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001957 semsg(_("E824: Incompatible undo file: %s"), file_name);
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001958 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001959 }
1960
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001961 if (undo_read(&bi, read_hash, (size_t)UNDO_HASH_SIZE) == FAIL)
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001962 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001963 corruption_error("hash", file_name);
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001964 goto error;
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001965 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001966 line_count = (linenr_T)undo_read_4c(&bi);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001967 if (memcmp(hash, read_hash, UNDO_HASH_SIZE) != 0
1968 || line_count != curbuf->b_ml.ml_line_count)
1969 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001970 if (p_verbose > 0 || name != NULL)
1971 {
Bram Moolenaar504a8212010-05-30 17:17:42 +02001972 if (name == NULL)
1973 verbose_enter();
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001974 give_warning((char_u *)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001975 _("File contents changed, cannot use undo info"), TRUE);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001976 if (name == NULL)
1977 verbose_leave();
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001978 }
1979 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001980 }
1981
Bram Moolenaare38eab22019-12-05 21:50:01 +01001982 // Read undo data for "U" command.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001983 str_len = undo_read_4c(&bi);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001984 if (str_len < 0)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001985 goto error;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001986 if (str_len > 0)
Bram Moolenaarccae4672019-01-04 15:09:57 +01001987 {
1988 line_ptr.ul_line = read_string_decrypt(&bi, str_len);
1989 line_ptr.ul_len = str_len + 1;
1990 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001991 line_lnum = (linenr_T)undo_read_4c(&bi);
1992 line_colnr = (colnr_T)undo_read_4c(&bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001993 if (line_lnum < 0 || line_colnr < 0)
1994 {
1995 corruption_error("line lnum/col", file_name);
1996 goto error;
1997 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001998
Bram Moolenaare38eab22019-12-05 21:50:01 +01001999 // Begin general undo data
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002000 old_header_seq = undo_read_4c(&bi);
2001 new_header_seq = undo_read_4c(&bi);
2002 cur_header_seq = undo_read_4c(&bi);
2003 num_head = undo_read_4c(&bi);
2004 seq_last = undo_read_4c(&bi);
2005 seq_cur = undo_read_4c(&bi);
2006 seq_time = undo_read_time(&bi);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002007
Bram Moolenaare38eab22019-12-05 21:50:01 +01002008 // Optional header fields.
Bram Moolenaar69154f22010-07-18 21:42:34 +02002009 for (;;)
2010 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002011 int len = undo_read_byte(&bi);
Bram Moolenaar69154f22010-07-18 21:42:34 +02002012 int what;
Bram Moolenaara800b422010-06-27 01:15:55 +02002013
Bram Moolenaar69154f22010-07-18 21:42:34 +02002014 if (len == 0 || len == EOF)
2015 break;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002016 what = undo_read_byte(&bi);
Bram Moolenaar69154f22010-07-18 21:42:34 +02002017 switch (what)
2018 {
2019 case UF_LAST_SAVE_NR:
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002020 last_save_nr = undo_read_4c(&bi);
Bram Moolenaara800b422010-06-27 01:15:55 +02002021 break;
Bram Moolenaar69154f22010-07-18 21:42:34 +02002022 default:
Bram Moolenaare38eab22019-12-05 21:50:01 +01002023 // field not supported, skip
Bram Moolenaar69154f22010-07-18 21:42:34 +02002024 while (--len >= 0)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002025 (void)undo_read_byte(&bi);
Bram Moolenaara800b422010-06-27 01:15:55 +02002026 }
Bram Moolenaar69154f22010-07-18 21:42:34 +02002027 }
Bram Moolenaara800b422010-06-27 01:15:55 +02002028
Bram Moolenaare38eab22019-12-05 21:50:01 +01002029 // uhp_table will store the freshly created undo headers we allocate
2030 // until we insert them into curbuf. The table remains sorted by the
2031 // sequence numbers of the headers.
2032 // When there are no headers uhp_table is NULL.
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002033 if (num_head > 0)
2034 {
Bram Moolenaar3eb16372017-02-26 18:11:36 +01002035 if (num_head < LONG_MAX / (long)sizeof(u_header_T *))
Bram Moolenaarc799fe22019-05-28 23:08:19 +02002036 uhp_table = U_ALLOC_LINE(num_head * sizeof(u_header_T *));
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002037 if (uhp_table == NULL)
2038 goto error;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002039 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002040
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002041 while ((c = undo_read_2c(&bi)) == UF_HEADER_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002042 {
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02002043 if (num_read_uhps >= num_head)
2044 {
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02002045 corruption_error("num_head too small", file_name);
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02002046 goto error;
2047 }
2048
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002049 uhp = unserialize_uhp(&bi, file_name);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02002050 if (uhp == NULL)
2051 goto error;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02002052 uhp_table[num_read_uhps++] = uhp;
2053 }
2054
2055 if (num_read_uhps != num_head)
2056 {
2057 corruption_error("num_head", file_name);
2058 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002059 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02002060 if (c != UF_HEADER_END_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002061 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02002062 corruption_error("end marker", file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002063 goto error;
2064 }
2065
Bram Moolenaar9db58062010-05-29 20:33:07 +02002066#ifdef U_DEBUG
Bram Moolenaarc799fe22019-05-28 23:08:19 +02002067 uhp_table_used = alloc_clear(sizeof(int) * num_head + 1);
Bram Moolenaar9db58062010-05-29 20:33:07 +02002068# define SET_FLAG(j) ++uhp_table_used[j]
2069#else
2070# define SET_FLAG(j)
2071#endif
2072
Bram Moolenaare38eab22019-12-05 21:50:01 +01002073 // We have put all of the headers into a table. Now we iterate through the
2074 // table and swizzle each sequence number we have stored in uh_*_seq into
2075 // a pointer corresponding to the header with that sequence number.
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002076 for (i = 0; i < num_head; i++)
2077 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002078 uhp = uhp_table[i];
2079 if (uhp == NULL)
2080 continue;
2081 for (j = 0; j < num_head; j++)
2082 if (uhp_table[j] != NULL && i != j
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002083 && uhp_table[i]->uh_seq == uhp_table[j]->uh_seq)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002084 {
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02002085 corruption_error("duplicate uh_seq", file_name);
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002086 goto error;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02002087 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002088 for (j = 0; j < num_head; j++)
2089 if (uhp_table[j] != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002090 && uhp_table[j]->uh_seq == uhp->uh_next.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002091 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002092 uhp->uh_next.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02002093 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002094 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002095 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002096 for (j = 0; j < num_head; j++)
2097 if (uhp_table[j] != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002098 && uhp_table[j]->uh_seq == uhp->uh_prev.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002099 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002100 uhp->uh_prev.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02002101 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002102 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002103 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002104 for (j = 0; j < num_head; j++)
2105 if (uhp_table[j] != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002106 && uhp_table[j]->uh_seq == uhp->uh_alt_next.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002107 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002108 uhp->uh_alt_next.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02002109 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002110 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002111 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002112 for (j = 0; j < num_head; j++)
2113 if (uhp_table[j] != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002114 && uhp_table[j]->uh_seq == uhp->uh_alt_prev.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002115 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002116 uhp->uh_alt_prev.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02002117 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002118 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002119 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002120 if (old_header_seq > 0 && old_idx < 0 && uhp->uh_seq == old_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002121 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002122 old_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002123 SET_FLAG(i);
2124 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002125 if (new_header_seq > 0 && new_idx < 0 && uhp->uh_seq == new_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002126 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002127 new_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002128 SET_FLAG(i);
2129 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002130 if (cur_header_seq > 0 && cur_idx < 0 && uhp->uh_seq == cur_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002131 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002132 cur_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002133 SET_FLAG(i);
2134 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002135 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02002136
Bram Moolenaare38eab22019-12-05 21:50:01 +01002137 // Now that we have read the undo info successfully, free the current undo
2138 // info and use the info from the file.
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002139 u_blockfree(curbuf);
Bram Moolenaar9db58062010-05-29 20:33:07 +02002140 curbuf->b_u_oldhead = old_idx < 0 ? NULL : uhp_table[old_idx];
2141 curbuf->b_u_newhead = new_idx < 0 ? NULL : uhp_table[new_idx];
2142 curbuf->b_u_curhead = cur_idx < 0 ? NULL : uhp_table[cur_idx];
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002143 curbuf->b_u_line_ptr = line_ptr;
2144 curbuf->b_u_line_lnum = line_lnum;
2145 curbuf->b_u_line_colnr = line_colnr;
2146 curbuf->b_u_numhead = num_head;
2147 curbuf->b_u_seq_last = seq_last;
2148 curbuf->b_u_seq_cur = seq_cur;
Bram Moolenaara800b422010-06-27 01:15:55 +02002149 curbuf->b_u_time_cur = seq_time;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002150 curbuf->b_u_save_nr_last = last_save_nr;
Bram Moolenaardba01a02010-11-03 19:32:42 +01002151 curbuf->b_u_save_nr_cur = last_save_nr;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02002152
2153 curbuf->b_u_synced = TRUE;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002154 vim_free(uhp_table);
Bram Moolenaar9db58062010-05-29 20:33:07 +02002155
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002156#ifdef U_DEBUG
Bram Moolenaar9db58062010-05-29 20:33:07 +02002157 for (i = 0; i < num_head; ++i)
2158 if (uhp_table_used[i] == 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002159 semsg("uhp_table entry %ld not used, leaking memory", i);
Bram Moolenaar9db58062010-05-29 20:33:07 +02002160 vim_free(uhp_table_used);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002161 u_check(TRUE);
2162#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02002163
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002164 if (name != NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002165 smsg(_("Finished reading undo file %s"), file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002166 goto theend;
2167
2168error:
Bram Moolenaarccae4672019-01-04 15:09:57 +01002169 vim_free(line_ptr.ul_line);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002170 if (uhp_table != NULL)
2171 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002172 for (i = 0; i < num_read_uhps; i++)
2173 if (uhp_table[i] != NULL)
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02002174 u_free_uhp(uhp_table[i]);
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002175 vim_free(uhp_table);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002176 }
2177
2178theend:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002179#ifdef FEAT_CRYPT
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002180 if (bi.bi_state != NULL)
2181 crypt_free_state(bi.bi_state);
2182 vim_free(bi.bi_buffer);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002183#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002184 if (fp != NULL)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002185 fclose(fp);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002186 if (file_name != name)
2187 vim_free(file_name);
2188 return;
2189}
2190
Bram Moolenaare38eab22019-12-05 21:50:01 +01002191#endif // FEAT_PERSISTENT_UNDO
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002192
2193
Bram Moolenaar071d4272004-06-13 20:20:40 +00002194/*
2195 * If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible).
2196 * If 'cpoptions' does not contain 'u': Always undo.
2197 */
2198 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002199u_undo(int count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002200{
2201 /*
2202 * If we get an undo command while executing a macro, we behave like the
2203 * original vi. If this happens twice in one macro the result will not
2204 * be compatible.
2205 */
2206 if (curbuf->b_u_synced == FALSE)
2207 {
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002208 u_sync(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002209 count = 1;
2210 }
2211
2212 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
2213 undo_undoes = TRUE;
2214 else
2215 undo_undoes = !undo_undoes;
2216 u_doit(count);
2217}
2218
2219/*
2220 * If 'cpoptions' contains 'u': Repeat the previous undo or redo.
2221 * If 'cpoptions' does not contain 'u': Always redo.
2222 */
2223 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002224u_redo(int count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002225{
2226 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
2227 undo_undoes = FALSE;
2228 u_doit(count);
2229}
2230
2231/*
2232 * Undo or redo, depending on 'undo_undoes', 'count' times.
2233 */
2234 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002235u_doit(int startcount)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002236{
Bram Moolenaarca003e12006-03-17 23:19:38 +00002237 int count = startcount;
2238
Bram Moolenaar8ada17c2006-01-19 22:16:24 +00002239 if (!undo_allowed())
Bram Moolenaar071d4272004-06-13 20:20:40 +00002240 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002241
2242 u_newcount = 0;
2243 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00002244 if (curbuf->b_ml.ml_flags & ML_EMPTY)
2245 u_oldcount = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002246 while (count--)
2247 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002248 // Do the change warning now, so that it triggers FileChangedRO when
2249 // needed. This may cause the file to be reloaded, that must happen
2250 // before we do anything, because it may change curbuf->b_u_curhead
2251 // and more.
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002252 change_warning(0);
2253
Bram Moolenaar071d4272004-06-13 20:20:40 +00002254 if (undo_undoes)
2255 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002256 if (curbuf->b_u_curhead == NULL) // first undo
Bram Moolenaar071d4272004-06-13 20:20:40 +00002257 curbuf->b_u_curhead = curbuf->b_u_newhead;
Bram Moolenaare38eab22019-12-05 21:50:01 +01002258 else if (get_undolevel() > 0) // multi level undo
2259 // get next undo
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002260 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaare38eab22019-12-05 21:50:01 +01002261 // nothing to undo
Bram Moolenaar071d4272004-06-13 20:20:40 +00002262 if (curbuf->b_u_numhead == 0 || curbuf->b_u_curhead == NULL)
2263 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002264 // stick curbuf->b_u_curhead at end
Bram Moolenaar071d4272004-06-13 20:20:40 +00002265 curbuf->b_u_curhead = curbuf->b_u_oldhead;
2266 beep_flush();
Bram Moolenaarca003e12006-03-17 23:19:38 +00002267 if (count == startcount - 1)
2268 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002269 msg(_("Already at oldest change"));
Bram Moolenaarca003e12006-03-17 23:19:38 +00002270 return;
2271 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002272 break;
2273 }
2274
Bram Moolenaarca003e12006-03-17 23:19:38 +00002275 u_undoredo(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002276 }
2277 else
2278 {
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +01002279 if (curbuf->b_u_curhead == NULL || get_undolevel() <= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002280 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002281 beep_flush(); // nothing to redo
Bram Moolenaarca003e12006-03-17 23:19:38 +00002282 if (count == startcount - 1)
2283 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002284 msg(_("Already at newest change"));
Bram Moolenaarca003e12006-03-17 23:19:38 +00002285 return;
2286 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002287 break;
2288 }
2289
Bram Moolenaarca003e12006-03-17 23:19:38 +00002290 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002291
Bram Moolenaare38eab22019-12-05 21:50:01 +01002292 // Advance for next redo. Set "newhead" when at the end of the
2293 // redoable changes.
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002294 if (curbuf->b_u_curhead->uh_prev.ptr == NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002295 curbuf->b_u_newhead = curbuf->b_u_curhead;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002296 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002297 }
2298 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00002299 u_undo_end(undo_undoes, FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002300}
2301
Bram Moolenaar1e607892006-03-13 22:15:53 +00002302/*
2303 * Undo or redo over the timeline.
2304 * When "step" is negative go back in time, otherwise goes forward in time.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002305 * When "sec" is FALSE make "step" steps, when "sec" is TRUE use "step" as
2306 * seconds.
Bram Moolenaar730cde92010-06-27 05:18:54 +02002307 * When "file" is TRUE use "step" as a number of file writes.
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002308 * When "absolute" is TRUE use "step" as the sequence number to jump to.
2309 * "sec" must be FALSE then.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002310 */
2311 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002312undo_time(
2313 long step,
2314 int sec,
2315 int file,
2316 int absolute)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002317{
2318 long target;
2319 long closest;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002320 long closest_start;
2321 long closest_seq = 0;
2322 long val;
Bram Moolenaarce46d932018-01-30 22:46:06 +01002323 u_header_T *uhp = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002324 u_header_T *last;
2325 int mark;
Bram Moolenaar1f3601e2019-04-26 20:33:00 +02002326 int nomark = 0; // shut up compiler
Bram Moolenaar1e607892006-03-13 22:15:53 +00002327 int round;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002328 int dosec = sec;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002329 int dofile = file;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002330 int above = FALSE;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002331 int did_undo = TRUE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002332
Bram Moolenaare38eab22019-12-05 21:50:01 +01002333 // First make sure the current undoable change is synced.
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00002334 if (curbuf->b_u_synced == FALSE)
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002335 u_sync(TRUE);
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00002336
Bram Moolenaar1e607892006-03-13 22:15:53 +00002337 u_newcount = 0;
2338 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00002339 if (curbuf->b_ml.ml_flags & ML_EMPTY)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002340 u_oldcount = -1;
2341
Bram Moolenaare38eab22019-12-05 21:50:01 +01002342 // "target" is the node below which we want to be.
2343 // Init "closest" to a value we can't reach.
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002344 if (absolute)
2345 {
Bram Moolenaarce46d932018-01-30 22:46:06 +01002346 target = step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002347 closest = -1;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002348 }
Bram Moolenaarca003e12006-03-17 23:19:38 +00002349 else
Bram Moolenaar1e607892006-03-13 22:15:53 +00002350 {
Bram Moolenaar730cde92010-06-27 05:18:54 +02002351 if (dosec)
Bram Moolenaarcbd4de42017-01-07 16:14:57 +01002352 target = (long)(curbuf->b_u_time_cur) + step;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002353 else if (dofile)
2354 {
2355 if (step < 0)
2356 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002357 // Going back to a previous write. If there were changes after
2358 // the last write, count that as moving one file-write, so
2359 // that ":earlier 1f" undoes all changes since the last save.
Bram Moolenaar730cde92010-06-27 05:18:54 +02002360 uhp = curbuf->b_u_curhead;
2361 if (uhp != NULL)
2362 uhp = uhp->uh_next.ptr;
2363 else
2364 uhp = curbuf->b_u_newhead;
2365 if (uhp != NULL && uhp->uh_save_nr != 0)
Bram Moolenaare38eab22019-12-05 21:50:01 +01002366 // "uh_save_nr" was set in the last block, that means
2367 // there were no changes since the last write
Bram Moolenaar730cde92010-06-27 05:18:54 +02002368 target = curbuf->b_u_save_nr_cur + step;
2369 else
Bram Moolenaare38eab22019-12-05 21:50:01 +01002370 // count the changes since the last write as one step
Bram Moolenaar730cde92010-06-27 05:18:54 +02002371 target = curbuf->b_u_save_nr_cur + step + 1;
2372 if (target <= 0)
Bram Moolenaare38eab22019-12-05 21:50:01 +01002373 // Go to before first write: before the oldest change. Use
2374 // the sequence number for that.
Bram Moolenaar730cde92010-06-27 05:18:54 +02002375 dofile = FALSE;
2376 }
2377 else
2378 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002379 // Moving forward to a newer write.
Bram Moolenaar730cde92010-06-27 05:18:54 +02002380 target = curbuf->b_u_save_nr_cur + step;
2381 if (target > curbuf->b_u_save_nr_last)
2382 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002383 // Go to after last write: after the latest change. Use
2384 // the sequence number for that.
Bram Moolenaar730cde92010-06-27 05:18:54 +02002385 target = curbuf->b_u_seq_last + 1;
2386 dofile = FALSE;
2387 }
2388 }
2389 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002390 else
2391 target = curbuf->b_u_seq_cur + step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002392 if (step < 0)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002393 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002394 if (target < 0)
2395 target = 0;
2396 closest = -1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002397 }
2398 else
2399 {
Bram Moolenaar730cde92010-06-27 05:18:54 +02002400 if (dosec)
Bram Moolenaarcbd4de42017-01-07 16:14:57 +01002401 closest = (long)(vim_time() + 1);
Bram Moolenaar730cde92010-06-27 05:18:54 +02002402 else if (dofile)
2403 closest = curbuf->b_u_save_nr_last + 2;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002404 else
2405 closest = curbuf->b_u_seq_last + 2;
2406 if (target >= closest)
2407 target = closest - 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002408 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002409 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002410 closest_start = closest;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002411 closest_seq = curbuf->b_u_seq_cur;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002412
Bram Moolenaare38eab22019-12-05 21:50:01 +01002413 // When "target" is 0; Back to origin.
Bram Moolenaarce46d932018-01-30 22:46:06 +01002414 if (target == 0)
Bram Moolenaar059fd012018-01-31 14:25:53 +01002415 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002416 mark = lastmark; // avoid that GCC complains
Bram Moolenaar059fd012018-01-31 14:25:53 +01002417 goto target_zero;
2418 }
Bram Moolenaarce46d932018-01-30 22:46:06 +01002419
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002420 /*
2421 * May do this twice:
Bram Moolenaar1e607892006-03-13 22:15:53 +00002422 * 1. Search for "target", update "closest" to the best match found.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002423 * 2. If "target" not found search for "closest".
2424 *
2425 * When using the closest time we use the sequence number in the second
2426 * round, because there may be several entries with the same time.
2427 */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002428 for (round = 1; round <= 2; ++round)
2429 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002430 // Find the path from the current state to where we want to go. The
2431 // desired state can be anywhere in the undo tree, need to go all over
2432 // it. We put "nomark" in uh_walk where we have been without success,
2433 // "mark" where it could possibly be.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002434 mark = ++lastmark;
2435 nomark = ++lastmark;
2436
Bram Moolenaare38eab22019-12-05 21:50:01 +01002437 if (curbuf->b_u_curhead == NULL) // at leaf of the tree
Bram Moolenaar1e607892006-03-13 22:15:53 +00002438 uhp = curbuf->b_u_newhead;
2439 else
2440 uhp = curbuf->b_u_curhead;
2441
2442 while (uhp != NULL)
2443 {
2444 uhp->uh_walk = mark;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002445 if (dosec)
Bram Moolenaarcbd4de42017-01-07 16:14:57 +01002446 val = (long)(uhp->uh_time);
Bram Moolenaar730cde92010-06-27 05:18:54 +02002447 else if (dofile)
2448 val = uhp->uh_save_nr;
2449 else
2450 val = uhp->uh_seq;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002451
Bram Moolenaar730cde92010-06-27 05:18:54 +02002452 if (round == 1 && !(dofile && val == 0))
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002453 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002454 // Remember the header that is closest to the target.
2455 // It must be at least in the right direction (checked with
2456 // "b_u_seq_cur"). When the timestamp is equal find the
2457 // highest/lowest sequence number.
Bram Moolenaarca003e12006-03-17 23:19:38 +00002458 if ((step < 0 ? uhp->uh_seq <= curbuf->b_u_seq_cur
2459 : uhp->uh_seq > curbuf->b_u_seq_cur)
2460 && ((dosec && val == closest)
2461 ? (step < 0
2462 ? uhp->uh_seq < closest_seq
2463 : uhp->uh_seq > closest_seq)
2464 : closest == closest_start
2465 || (val > target
2466 ? (closest > target
2467 ? val - target <= closest - target
2468 : val - target <= target - closest)
2469 : (closest > target
2470 ? target - val <= closest - target
2471 : target - val <= target - closest))))
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002472 {
2473 closest = val;
2474 closest_seq = uhp->uh_seq;
2475 }
2476 }
2477
Bram Moolenaare38eab22019-12-05 21:50:01 +01002478 // Quit searching when we found a match. But when searching for a
2479 // time we need to continue looking for the best uh_seq.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002480 if (target == val && !dosec)
Bram Moolenaar730cde92010-06-27 05:18:54 +02002481 {
2482 target = uhp->uh_seq;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002483 break;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002484 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002485
Bram Moolenaare38eab22019-12-05 21:50:01 +01002486 // go down in the tree if we haven't been there
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002487 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
2488 && uhp->uh_prev.ptr->uh_walk != mark)
2489 uhp = uhp->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002490
Bram Moolenaare38eab22019-12-05 21:50:01 +01002491 // go to alternate branch if we haven't been there
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002492 else if (uhp->uh_alt_next.ptr != NULL
2493 && uhp->uh_alt_next.ptr->uh_walk != nomark
2494 && uhp->uh_alt_next.ptr->uh_walk != mark)
2495 uhp = uhp->uh_alt_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002496
Bram Moolenaare38eab22019-12-05 21:50:01 +01002497 // go up in the tree if we haven't been there and we are at the
2498 // start of alternate branches
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002499 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
2500 && uhp->uh_next.ptr->uh_walk != nomark
2501 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002502 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002503 // If still at the start we don't go through this change.
Bram Moolenaardb552d602006-03-23 22:59:57 +00002504 if (uhp == curbuf->b_u_curhead)
2505 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002506 uhp = uhp->uh_next.ptr;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002507 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002508
2509 else
2510 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002511 // need to backtrack; mark this node as useless
Bram Moolenaar1e607892006-03-13 22:15:53 +00002512 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002513 if (uhp->uh_alt_prev.ptr != NULL)
2514 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002515 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002516 uhp = uhp->uh_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002517 }
2518 }
2519
Bram Moolenaare38eab22019-12-05 21:50:01 +01002520 if (uhp != NULL) // found it
Bram Moolenaar1e607892006-03-13 22:15:53 +00002521 break;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002522
2523 if (absolute)
2524 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002525 semsg(_("E830: Undo number %ld not found"), step);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002526 return;
2527 }
2528
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002529 if (closest == closest_start)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002530 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002531 if (step < 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002532 msg(_("Already at oldest change"));
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002533 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002534 msg(_("Already at newest change"));
Bram Moolenaar1e607892006-03-13 22:15:53 +00002535 return;
2536 }
2537
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002538 target = closest_seq;
2539 dosec = FALSE;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002540 dofile = FALSE;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002541 if (step < 0)
Bram Moolenaare38eab22019-12-05 21:50:01 +01002542 above = TRUE; // stop above the header
Bram Moolenaar1e607892006-03-13 22:15:53 +00002543 }
2544
Bram Moolenaar059fd012018-01-31 14:25:53 +01002545target_zero:
Bram Moolenaare38eab22019-12-05 21:50:01 +01002546 // If we found it: Follow the path to go to where we want to be.
Bram Moolenaarce46d932018-01-30 22:46:06 +01002547 if (uhp != NULL || target == 0)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002548 {
2549 /*
2550 * First go up the tree as much as needed.
2551 */
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002552 while (!got_int)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002553 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002554 // Do the change warning now, for the same reason as above.
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002555 change_warning(0);
2556
Bram Moolenaar1e607892006-03-13 22:15:53 +00002557 uhp = curbuf->b_u_curhead;
2558 if (uhp == NULL)
2559 uhp = curbuf->b_u_newhead;
2560 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002561 uhp = uhp->uh_next.ptr;
Bram Moolenaarce46d932018-01-30 22:46:06 +01002562 if (uhp == NULL || (target > 0 && uhp->uh_walk != mark)
Bram Moolenaarca003e12006-03-17 23:19:38 +00002563 || (uhp->uh_seq == target && !above))
Bram Moolenaar1e607892006-03-13 22:15:53 +00002564 break;
2565 curbuf->b_u_curhead = uhp;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002566 u_undoredo(TRUE);
Bram Moolenaarce46d932018-01-30 22:46:06 +01002567 if (target > 0)
Bram Moolenaare38eab22019-12-05 21:50:01 +01002568 uhp->uh_walk = nomark; // don't go back down here
Bram Moolenaar1e607892006-03-13 22:15:53 +00002569 }
2570
Bram Moolenaare38eab22019-12-05 21:50:01 +01002571 // When back to origin, redo is not needed.
Bram Moolenaarce46d932018-01-30 22:46:06 +01002572 if (target > 0)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002573 {
Bram Moolenaarce46d932018-01-30 22:46:06 +01002574 /*
2575 * And now go down the tree (redo), branching off where needed.
2576 */
2577 while (!got_int)
2578 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002579 // Do the change warning now, for the same reason as above.
Bram Moolenaarce46d932018-01-30 22:46:06 +01002580 change_warning(0);
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002581
Bram Moolenaarce46d932018-01-30 22:46:06 +01002582 uhp = curbuf->b_u_curhead;
2583 if (uhp == NULL)
2584 break;
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002585
Bram Moolenaare38eab22019-12-05 21:50:01 +01002586 // Go back to the first branch with a mark.
Bram Moolenaarce46d932018-01-30 22:46:06 +01002587 while (uhp->uh_alt_prev.ptr != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002588 && uhp->uh_alt_prev.ptr->uh_walk == mark)
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002589 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaarce46d932018-01-30 22:46:06 +01002590
Bram Moolenaare38eab22019-12-05 21:50:01 +01002591 // Find the last branch with a mark, that's the one.
Bram Moolenaarce46d932018-01-30 22:46:06 +01002592 last = uhp;
2593 while (last->uh_alt_next.ptr != NULL
2594 && last->uh_alt_next.ptr->uh_walk == mark)
2595 last = last->uh_alt_next.ptr;
2596 if (last != uhp)
2597 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002598 // Make the used branch the first entry in the list of
2599 // alternatives to make "u" and CTRL-R take this branch.
Bram Moolenaarce46d932018-01-30 22:46:06 +01002600 while (uhp->uh_alt_prev.ptr != NULL)
2601 uhp = uhp->uh_alt_prev.ptr;
2602 if (last->uh_alt_next.ptr != NULL)
2603 last->uh_alt_next.ptr->uh_alt_prev.ptr =
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002604 last->uh_alt_prev.ptr;
Bram Moolenaarce46d932018-01-30 22:46:06 +01002605 last->uh_alt_prev.ptr->uh_alt_next.ptr =
2606 last->uh_alt_next.ptr;
2607 last->uh_alt_prev.ptr = NULL;
2608 last->uh_alt_next.ptr = uhp;
2609 uhp->uh_alt_prev.ptr = last;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002610
Bram Moolenaarce46d932018-01-30 22:46:06 +01002611 if (curbuf->b_u_oldhead == uhp)
2612 curbuf->b_u_oldhead = last;
2613 uhp = last;
2614 if (uhp->uh_next.ptr != NULL)
2615 uhp->uh_next.ptr->uh_prev.ptr = uhp;
2616 }
2617 curbuf->b_u_curhead = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002618
Bram Moolenaarce46d932018-01-30 22:46:06 +01002619 if (uhp->uh_walk != mark)
Bram Moolenaare38eab22019-12-05 21:50:01 +01002620 break; // must have reached the target
Bram Moolenaar1e607892006-03-13 22:15:53 +00002621
Bram Moolenaare38eab22019-12-05 21:50:01 +01002622 // Stop when going backwards in time and didn't find the exact
2623 // header we were looking for.
Bram Moolenaarce46d932018-01-30 22:46:06 +01002624 if (uhp->uh_seq == target && above)
2625 {
2626 curbuf->b_u_seq_cur = target - 1;
2627 break;
2628 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002629
Bram Moolenaarce46d932018-01-30 22:46:06 +01002630 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002631
Bram Moolenaare38eab22019-12-05 21:50:01 +01002632 // Advance "curhead" to below the header we last used. If it
2633 // becomes NULL then we need to set "newhead" to this leaf.
Bram Moolenaarce46d932018-01-30 22:46:06 +01002634 if (uhp->uh_prev.ptr == NULL)
2635 curbuf->b_u_newhead = uhp;
2636 curbuf->b_u_curhead = uhp->uh_prev.ptr;
2637 did_undo = FALSE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002638
Bram Moolenaare38eab22019-12-05 21:50:01 +01002639 if (uhp->uh_seq == target) // found it!
Bram Moolenaarce46d932018-01-30 22:46:06 +01002640 break;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002641
Bram Moolenaarce46d932018-01-30 22:46:06 +01002642 uhp = uhp->uh_prev.ptr;
2643 if (uhp == NULL || uhp->uh_walk != mark)
2644 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002645 // Need to redo more but can't find it...
Bram Moolenaarce46d932018-01-30 22:46:06 +01002646 internal_error("undo_time()");
2647 break;
2648 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002649 }
2650 }
2651 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00002652 u_undo_end(did_undo, absolute);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002653}
2654
2655/*
2656 * u_undoredo: common code for undo and redo
2657 *
2658 * The lines in the file are replaced by the lines in the entry list at
2659 * curbuf->b_u_curhead. The replaced lines in the file are saved in the entry
2660 * list for the next undo/redo.
Bram Moolenaarca003e12006-03-17 23:19:38 +00002661 *
2662 * When "undo" is TRUE we go up in the tree, when FALSE we go down.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002663 */
2664 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002665u_undoredo(int undo)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002666{
Bram Moolenaarccae4672019-01-04 15:09:57 +01002667 undoline_T *newarray = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002668 linenr_T oldsize;
2669 linenr_T newsize;
2670 linenr_T top, bot;
2671 linenr_T lnum;
2672 linenr_T newlnum = MAXLNUM;
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002673 pos_T new_curpos = curwin->w_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002674 long i;
2675 u_entry_T *uep, *nuep;
2676 u_entry_T *newlist = NULL;
2677 int old_flags;
2678 int new_flags;
2679 pos_T namedm[NMARKS];
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002680 visualinfo_T visualinfo;
Bram Moolenaare38eab22019-12-05 21:50:01 +01002681 int empty_buffer; // buffer became empty
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002682 u_header_T *curhead = curbuf->b_u_curhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002683
Bram Moolenaare38eab22019-12-05 21:50:01 +01002684 // Don't want autocommands using the undo structures here, they are
2685 // invalid till the end.
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002686 block_autocmds();
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002687
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002688#ifdef U_DEBUG
2689 u_check(FALSE);
2690#endif
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002691 old_flags = curhead->uh_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002692 new_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
2693 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
2694 setpcmark();
2695
2696 /*
2697 * save marks before undo/redo
2698 */
2699 mch_memmove(namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002700 visualinfo = curbuf->b_visual;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002701 curbuf->b_op_start.lnum = curbuf->b_ml.ml_line_count;
2702 curbuf->b_op_start.col = 0;
2703 curbuf->b_op_end.lnum = 0;
2704 curbuf->b_op_end.col = 0;
2705
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002706 for (uep = curhead->uh_entry; uep != NULL; uep = nuep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002707 {
2708 top = uep->ue_top;
2709 bot = uep->ue_bot;
2710 if (bot == 0)
2711 bot = curbuf->b_ml.ml_line_count + 1;
2712 if (top > curbuf->b_ml.ml_line_count || top >= bot
2713 || bot > curbuf->b_ml.ml_line_count + 1)
2714 {
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002715 unblock_autocmds();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002716 iemsg(_("E438: u_undo: line numbers wrong"));
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002717 changed(); // don't want UNCHANGED now
Bram Moolenaar071d4272004-06-13 20:20:40 +00002718 return;
2719 }
2720
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002721 oldsize = bot - top - 1; // number of lines before undo
2722 newsize = uep->ue_size; // number of lines after undo
Bram Moolenaar071d4272004-06-13 20:20:40 +00002723
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002724 // Decide about the cursor position, depending on what text changed.
2725 // Don't set it yet, it may be invalid if lines are going to be added.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002726 if (top < newlnum)
2727 {
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002728 // If the saved cursor is somewhere in this undo block, move it to
2729 // the remembered position. Makes "gwap" put the cursor back
2730 // where it was.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002731 lnum = curhead->uh_cursor.lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002732 if (lnum >= top && lnum <= top + newsize + 1)
2733 {
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002734 new_curpos = curhead->uh_cursor;
2735 newlnum = new_curpos.lnum - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002736 }
2737 else
2738 {
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002739 // Use the first line that actually changed. Avoids that
2740 // undoing auto-formatting puts the cursor in the previous
2741 // line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002742 for (i = 0; i < newsize && i < oldsize; ++i)
Bram Moolenaarccae4672019-01-04 15:09:57 +01002743 {
2744 char_u *p = ml_get(top + 1 + i);
2745
2746 if (curbuf->b_ml.ml_line_len != uep->ue_array[i].ul_len
Bram Moolenaar964b3742019-05-24 18:54:09 +02002747 || memcmp(uep->ue_array[i].ul_line, p,
2748 curbuf->b_ml.ml_line_len) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002749 break;
Bram Moolenaarccae4672019-01-04 15:09:57 +01002750 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002751 if (i == newsize && newlnum == MAXLNUM && uep->ue_next == NULL)
2752 {
2753 newlnum = top;
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002754 new_curpos.lnum = newlnum + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002755 }
2756 else if (i < newsize)
2757 {
2758 newlnum = top + i;
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002759 new_curpos.lnum = newlnum + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002760 }
2761 }
2762 }
2763
2764 empty_buffer = FALSE;
2765
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002766 /*
2767 * Delete the lines between top and bot and save them in newarray.
2768 */
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002769 if (oldsize > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002770 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02002771 if ((newarray = U_ALLOC_LINE(sizeof(undoline_T) * oldsize)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002772 {
Bram Moolenaarccae4672019-01-04 15:09:57 +01002773 do_outofmem_msg((long_u)(sizeof(undoline_T) * oldsize));
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002774
2775 // We have messed up the entry list, repair is impossible.
2776 // we have to free the rest of the list.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002777 while (uep != NULL)
2778 {
2779 nuep = uep->ue_next;
2780 u_freeentry(uep, uep->ue_size);
2781 uep = nuep;
2782 }
2783 break;
2784 }
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002785 // delete backwards, it goes faster in most cases
Bram Moolenaar071d4272004-06-13 20:20:40 +00002786 for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum)
2787 {
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002788 // what can we do when we run out of memory?
Bram Moolenaarccae4672019-01-04 15:09:57 +01002789 if (u_save_line(&newarray[i], lnum) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002790 do_outofmem_msg((long_u)0);
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002791 // remember we deleted the last line in the buffer, and a
2792 // dummy empty line will be inserted
Bram Moolenaar071d4272004-06-13 20:20:40 +00002793 if (curbuf->b_ml.ml_line_count == 1)
2794 empty_buffer = TRUE;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002795 ml_delete_flags(lnum, ML_DEL_UNDO);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002796 }
2797 }
Bram Moolenaar8d343302005-07-12 22:46:17 +00002798 else
2799 newarray = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002800
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002801 // make sure the cursor is on a valid line after the deletions
2802 check_cursor_lnum();
2803
2804 /*
2805 * Insert the lines in u_array between top and bot.
2806 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002807 if (newsize)
2808 {
2809 for (lnum = top, i = 0; i < newsize; ++i, ++lnum)
2810 {
Bram Moolenaarccae4672019-01-04 15:09:57 +01002811 // If the file is empty, there is an empty line 1 that we
2812 // should get rid of, by replacing it with the new line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002813 if (empty_buffer && lnum == 0)
Bram Moolenaar964b3742019-05-24 18:54:09 +02002814 ml_replace_len((linenr_T)1, uep->ue_array[i].ul_line,
2815 uep->ue_array[i].ul_len, TRUE, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002816 else
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002817 ml_append_flags(lnum, uep->ue_array[i].ul_line,
2818 (colnr_T)uep->ue_array[i].ul_len, ML_APPEND_UNDO);
Bram Moolenaarccae4672019-01-04 15:09:57 +01002819 vim_free(uep->ue_array[i].ul_line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002820 }
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002821 vim_free((char_u *)uep->ue_array);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002822 }
2823
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002824 // adjust marks
Bram Moolenaar071d4272004-06-13 20:20:40 +00002825 if (oldsize != newsize)
2826 {
2827 mark_adjust(top + 1, top + oldsize, (long)MAXLNUM,
2828 (long)newsize - (long)oldsize);
2829 if (curbuf->b_op_start.lnum > top + oldsize)
2830 curbuf->b_op_start.lnum += newsize - oldsize;
2831 if (curbuf->b_op_end.lnum > top + oldsize)
2832 curbuf->b_op_end.lnum += newsize - oldsize;
2833 }
2834
2835 changed_lines(top + 1, 0, bot, newsize - oldsize);
2836
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002837 // set '[ and '] mark
Bram Moolenaar071d4272004-06-13 20:20:40 +00002838 if (top + 1 < curbuf->b_op_start.lnum)
2839 curbuf->b_op_start.lnum = top + 1;
2840 if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum)
2841 curbuf->b_op_end.lnum = top + 1;
2842 else if (top + newsize > curbuf->b_op_end.lnum)
2843 curbuf->b_op_end.lnum = top + newsize;
2844
2845 u_newcount += newsize;
2846 u_oldcount += oldsize;
2847 uep->ue_size = oldsize;
2848 uep->ue_array = newarray;
2849 uep->ue_bot = top + newsize + 1;
2850
2851 /*
2852 * insert this entry in front of the new entry list
2853 */
2854 nuep = uep->ue_next;
2855 uep->ue_next = newlist;
2856 newlist = uep;
2857 }
2858
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002859 // Set the cursor to the desired position. Check that the line is valid.
2860 curwin->w_cursor = new_curpos;
2861 check_cursor_lnum();
2862
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002863 curhead->uh_entry = newlist;
2864 curhead->uh_flags = new_flags;
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01002865 if ((old_flags & UH_EMPTYBUF) && BUFEMPTY())
Bram Moolenaar071d4272004-06-13 20:20:40 +00002866 curbuf->b_ml.ml_flags |= ML_EMPTY;
2867 if (old_flags & UH_CHANGED)
2868 changed();
2869 else
Bram Moolenaar009b2592004-10-24 19:18:58 +00002870#ifdef FEAT_NETBEANS_INTG
Bram Moolenaare38eab22019-12-05 21:50:01 +01002871 // per netbeans undo rules, keep it as modified
Bram Moolenaar009b2592004-10-24 19:18:58 +00002872 if (!isNetbeansModified(curbuf))
2873#endif
Bram Moolenaarc024b462019-06-08 18:07:21 +02002874 unchanged(curbuf, FALSE, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002875
2876 /*
2877 * restore marks from before undo/redo
2878 */
2879 for (i = 0; i < NMARKS; ++i)
Bram Moolenaarf65aad52015-02-17 13:43:40 +01002880 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002881 if (curhead->uh_namedm[i].lnum != 0)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002882 curbuf->b_namedm[i] = curhead->uh_namedm[i];
Bram Moolenaarf65aad52015-02-17 13:43:40 +01002883 if (namedm[i].lnum != 0)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002884 curhead->uh_namedm[i] = namedm[i];
Bram Moolenaarf65aad52015-02-17 13:43:40 +01002885 else
2886 curhead->uh_namedm[i].lnum = 0;
2887 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002888 if (curhead->uh_visual.vi_start.lnum != 0)
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002889 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002890 curbuf->b_visual = curhead->uh_visual;
2891 curhead->uh_visual = visualinfo;
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002892 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002893
2894 /*
2895 * If the cursor is only off by one line, put it at the same position as
2896 * before starting the change (for the "o" command).
2897 * Otherwise the cursor should go to the first undone line.
2898 */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002899 if (curhead->uh_cursor.lnum + 1 == curwin->w_cursor.lnum
Bram Moolenaar071d4272004-06-13 20:20:40 +00002900 && curwin->w_cursor.lnum > 1)
2901 --curwin->w_cursor.lnum;
Bram Moolenaar0390ded2010-08-07 12:54:12 +02002902 if (curwin->w_cursor.lnum <= curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002903 {
Bram Moolenaar0390ded2010-08-07 12:54:12 +02002904 if (curhead->uh_cursor.lnum == curwin->w_cursor.lnum)
2905 {
2906 curwin->w_cursor.col = curhead->uh_cursor.col;
Bram Moolenaar0390ded2010-08-07 12:54:12 +02002907 if (virtual_active() && curhead->uh_cursor_vcol >= 0)
2908 coladvance((colnr_T)curhead->uh_cursor_vcol);
2909 else
2910 curwin->w_cursor.coladd = 0;
Bram Moolenaar0390ded2010-08-07 12:54:12 +02002911 }
2912 else
2913 beginline(BL_SOL | BL_FIX);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002914 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002915 else
2916 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002917 // We get here with the current cursor line being past the end (eg
2918 // after adding lines at the end of the file, and then undoing it).
2919 // check_cursor() will move the cursor to the last line. Move it to
2920 // the first column here.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002921 curwin->w_cursor.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002922 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002923 }
2924
Bram Moolenaare38eab22019-12-05 21:50:01 +01002925 // Make sure the cursor is on an existing line and column.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002926 check_cursor();
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002927
Bram Moolenaare38eab22019-12-05 21:50:01 +01002928 // Remember where we are for "g-" and ":earlier 10s".
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002929 curbuf->b_u_seq_cur = curhead->uh_seq;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002930 if (undo)
Bram Moolenaar80eaddd2017-11-11 23:37:08 +01002931 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002932 // We are below the previous undo. However, to make ":earlier 1s"
2933 // work we compute this as being just above the just undone change.
Bram Moolenaar80eaddd2017-11-11 23:37:08 +01002934 if (curhead->uh_next.ptr != NULL)
2935 curbuf->b_u_seq_cur = curhead->uh_next.ptr->uh_seq;
2936 else
2937 curbuf->b_u_seq_cur = 0;
2938 }
Bram Moolenaarca003e12006-03-17 23:19:38 +00002939
Bram Moolenaare38eab22019-12-05 21:50:01 +01002940 // Remember where we are for ":earlier 1f" and ":later 1f".
Bram Moolenaar730cde92010-06-27 05:18:54 +02002941 if (curhead->uh_save_nr != 0)
2942 {
2943 if (undo)
2944 curbuf->b_u_save_nr_cur = curhead->uh_save_nr - 1;
2945 else
2946 curbuf->b_u_save_nr_cur = curhead->uh_save_nr;
2947 }
2948
Bram Moolenaare38eab22019-12-05 21:50:01 +01002949 // The timestamp can be the same for multiple changes, just use the one of
2950 // the undone/redone change.
Bram Moolenaara800b422010-06-27 01:15:55 +02002951 curbuf->b_u_time_cur = curhead->uh_time;
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002952
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002953 unblock_autocmds();
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002954#ifdef U_DEBUG
2955 u_check(FALSE);
2956#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002957}
2958
2959/*
2960 * If we deleted or added lines, report the number of less/more lines.
2961 * Otherwise, report the number of changes (this may be incorrect
2962 * in some cases, but it's better than nothing).
2963 */
2964 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002965u_undo_end(
Bram Moolenaare38eab22019-12-05 21:50:01 +01002966 int did_undo, // just did an undo
2967 int absolute) // used ":undo N"
Bram Moolenaar071d4272004-06-13 20:20:40 +00002968{
Bram Moolenaar89d40322006-08-29 15:30:07 +00002969 char *msgstr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002970 u_header_T *uhp;
2971 char_u msgbuf[80];
Bram Moolenaar1e607892006-03-13 22:15:53 +00002972
Bram Moolenaar071d4272004-06-13 20:20:40 +00002973#ifdef FEAT_FOLDING
2974 if ((fdo_flags & FDO_UNDO) && KeyTyped)
2975 foldOpenCursor();
2976#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +00002977
Bram Moolenaare38eab22019-12-05 21:50:01 +01002978 if (global_busy // no messages now, wait until global is finished
2979 || !messaging()) // 'lazyredraw' set, don't do messages now
Bram Moolenaar1e607892006-03-13 22:15:53 +00002980 return;
2981
2982 if (curbuf->b_ml.ml_flags & ML_EMPTY)
2983 --u_newcount;
2984
2985 u_oldcount -= u_newcount;
2986 if (u_oldcount == -1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002987 msgstr = N_("more line");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002988 else if (u_oldcount < 0)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002989 msgstr = N_("more lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002990 else if (u_oldcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002991 msgstr = N_("line less");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002992 else if (u_oldcount > 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002993 msgstr = N_("fewer lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002994 else
2995 {
2996 u_oldcount = u_newcount;
2997 if (u_newcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002998 msgstr = N_("change");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002999 else
Bram Moolenaar89d40322006-08-29 15:30:07 +00003000 msgstr = N_("changes");
Bram Moolenaar1e607892006-03-13 22:15:53 +00003001 }
3002
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003003 if (curbuf->b_u_curhead != NULL)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00003004 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003005 // For ":undo N" we prefer a "after #N" message.
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003006 if (absolute && curbuf->b_u_curhead->uh_next.ptr != NULL)
Bram Moolenaardb552d602006-03-23 22:59:57 +00003007 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003008 uhp = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaardb552d602006-03-23 22:59:57 +00003009 did_undo = FALSE;
3010 }
3011 else if (did_undo)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00003012 uhp = curbuf->b_u_curhead;
3013 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003014 uhp = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00003015 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00003016 else
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003017 uhp = curbuf->b_u_newhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +00003018
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003019 if (uhp == NULL)
3020 *msgbuf = NUL;
3021 else
Bram Moolenaar52410572019-10-27 05:12:45 +01003022 add_time(msgbuf, sizeof(msgbuf), uhp->uh_time);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003023
Bram Moolenaarb2c03502010-07-02 20:20:09 +02003024#ifdef FEAT_CONCEAL
3025 {
3026 win_T *wp;
3027
3028 FOR_ALL_WINDOWS(wp)
3029 {
Bram Moolenaarf5963f72010-07-23 22:10:27 +02003030 if (wp->w_buffer == curbuf && wp->w_p_cole > 0)
Bram Moolenaarb2c03502010-07-02 20:20:09 +02003031 redraw_win_later(wp, NOT_VALID);
3032 }
3033 }
3034#endif
3035
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003036 smsg_attr_keep(0, _("%ld %s; %s #%ld %s"),
Bram Moolenaarca003e12006-03-17 23:19:38 +00003037 u_oldcount < 0 ? -u_oldcount : u_oldcount,
Bram Moolenaar89d40322006-08-29 15:30:07 +00003038 _(msgstr),
Bram Moolenaar433f7c82006-03-21 21:29:36 +00003039 did_undo ? _("before") : _("after"),
3040 uhp == NULL ? 0L : uhp->uh_seq,
3041 msgbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003042}
3043
3044/*
3045 * u_sync: stop adding to the current entry list
3046 */
3047 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003048u_sync(
Bram Moolenaare38eab22019-12-05 21:50:01 +01003049 int force) // Also sync when no_u_sync is set.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003050{
Bram Moolenaare38eab22019-12-05 21:50:01 +01003051 // Skip it when already synced or syncing is disabled.
Bram Moolenaar779b74b2006-04-10 14:55:34 +00003052 if (curbuf->b_u_synced || (!force && no_u_sync > 0))
3053 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003054#if defined(FEAT_XIM) && defined(FEAT_GUI_GTK)
Bram Moolenaar5c6dbcb2017-08-30 22:00:20 +02003055 if (p_imst == IM_ON_THE_SPOT && im_is_preediting())
Bram Moolenaare38eab22019-12-05 21:50:01 +01003056 return; // XIM is busy, don't break an undo sequence
Bram Moolenaar071d4272004-06-13 20:20:40 +00003057#endif
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +01003058 if (get_undolevel() < 0)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003059 curbuf->b_u_synced = TRUE; // no entries, nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00003060 else
3061 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003062 u_getbot(); // compute ue_bot of previous u_save
Bram Moolenaar071d4272004-06-13 20:20:40 +00003063 curbuf->b_u_curhead = NULL;
3064 }
3065}
3066
3067/*
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003068 * ":undolist": List the leafs of the undo tree
3069 */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003070 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003071ex_undolist(exarg_T *eap UNUSED)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003072{
3073 garray_T ga;
3074 u_header_T *uhp;
3075 int mark;
3076 int nomark;
3077 int changes = 1;
3078 int i;
3079
3080 /*
3081 * 1: walk the tree to find all leafs, put the info in "ga".
3082 * 2: sort the lines
3083 * 3: display the list
3084 */
3085 mark = ++lastmark;
3086 nomark = ++lastmark;
3087 ga_init2(&ga, (int)sizeof(char *), 20);
3088
3089 uhp = curbuf->b_u_oldhead;
3090 while (uhp != NULL)
3091 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003092 if (uhp->uh_prev.ptr == NULL && uhp->uh_walk != nomark
Bram Moolenaarca003e12006-03-17 23:19:38 +00003093 && uhp->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003094 {
3095 if (ga_grow(&ga, 1) == FAIL)
3096 break;
Bram Moolenaarea391762018-04-08 13:07:22 +02003097 vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7d ",
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003098 uhp->uh_seq, changes);
Bram Moolenaar52410572019-10-27 05:12:45 +01003099 add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff),
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003100 uhp->uh_time);
Bram Moolenaara800b422010-06-27 01:15:55 +02003101 if (uhp->uh_save_nr > 0)
3102 {
Bram Moolenaardba01a02010-11-03 19:32:42 +01003103 while (STRLEN(IObuff) < 33)
Bram Moolenaara800b422010-06-27 01:15:55 +02003104 STRCAT(IObuff, " ");
3105 vim_snprintf_add((char *)IObuff, IOSIZE,
3106 " %3ld", uhp->uh_save_nr);
3107 }
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003108 ((char_u **)(ga.ga_data))[ga.ga_len++] = vim_strsave(IObuff);
3109 }
3110
3111 uhp->uh_walk = mark;
3112
Bram Moolenaare38eab22019-12-05 21:50:01 +01003113 // go down in the tree if we haven't been there
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003114 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
3115 && uhp->uh_prev.ptr->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003116 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003117 uhp = uhp->uh_prev.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003118 ++changes;
3119 }
3120
Bram Moolenaare38eab22019-12-05 21:50:01 +01003121 // go to alternate branch if we haven't been there
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003122 else if (uhp->uh_alt_next.ptr != NULL
3123 && uhp->uh_alt_next.ptr->uh_walk != nomark
3124 && uhp->uh_alt_next.ptr->uh_walk != mark)
3125 uhp = uhp->uh_alt_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003126
Bram Moolenaare38eab22019-12-05 21:50:01 +01003127 // go up in the tree if we haven't been there and we are at the
3128 // start of alternate branches
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003129 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
3130 && uhp->uh_next.ptr->uh_walk != nomark
3131 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003132 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003133 uhp = uhp->uh_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003134 --changes;
3135 }
3136
3137 else
3138 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003139 // need to backtrack; mark this node as done
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003140 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003141 if (uhp->uh_alt_prev.ptr != NULL)
3142 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003143 else
3144 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003145 uhp = uhp->uh_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003146 --changes;
3147 }
3148 }
3149 }
3150
3151 if (ga.ga_len == 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01003152 msg(_("Nothing to undo"));
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003153 else
3154 {
3155 sort_strings((char_u **)ga.ga_data, ga.ga_len);
3156
3157 msg_start();
Bram Moolenaar32526b32019-01-19 17:43:09 +01003158 msg_puts_attr(_("number changes when saved"),
Bram Moolenaar8820b482017-03-16 17:23:31 +01003159 HL_ATTR(HLF_T));
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003160 for (i = 0; i < ga.ga_len && !got_int; ++i)
3161 {
3162 msg_putchar('\n');
3163 if (got_int)
3164 break;
Bram Moolenaar32526b32019-01-19 17:43:09 +01003165 msg_puts(((char **)ga.ga_data)[i]);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003166 }
3167 msg_end();
3168
3169 ga_clear_strings(&ga);
3170 }
3171}
3172
3173/*
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003174 * ":undojoin": continue adding to the last entry list
3175 */
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003176 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003177ex_undojoin(exarg_T *eap UNUSED)
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003178{
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003179 if (curbuf->b_u_newhead == NULL)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003180 return; // nothing changed before
Bram Moolenaar57657d82006-04-21 22:12:41 +00003181 if (curbuf->b_u_curhead != NULL)
3182 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003183 emsg(_("E790: undojoin is not allowed after undo"));
Bram Moolenaar57657d82006-04-21 22:12:41 +00003184 return;
3185 }
3186 if (!curbuf->b_u_synced)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003187 return; // already unsynced
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +01003188 if (get_undolevel() < 0)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003189 return; // no entries, nothing to do
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003190 else
Bram Moolenaare38eab22019-12-05 21:50:01 +01003191 // Append next change to the last entry
Bram Moolenaar5e4e1b12017-01-17 22:09:45 +01003192 curbuf->b_u_synced = FALSE;
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003193}
3194
3195/*
Bram Moolenaar59f931e2010-07-24 20:27:03 +02003196 * Called after writing or reloading the file and setting b_changed to FALSE.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003197 * Now an undo means that the buffer is modified.
3198 */
3199 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003200u_unchanged(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003201{
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003202 u_unch_branch(buf->b_u_oldhead);
3203 buf->b_did_warn = FALSE;
3204}
3205
Bram Moolenaar730cde92010-06-27 05:18:54 +02003206/*
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003207 * After reloading a buffer which was saved for 'undoreload': Find the first
3208 * line that was changed and set the cursor there.
3209 */
3210 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003211u_find_first_changed(void)
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003212{
3213 u_header_T *uhp = curbuf->b_u_newhead;
3214 u_entry_T *uep;
3215 linenr_T lnum;
3216
3217 if (curbuf->b_u_curhead != NULL || uhp == NULL)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003218 return; // undid something in an autocmd?
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003219
Bram Moolenaare38eab22019-12-05 21:50:01 +01003220 // Check that the last undo block was for the whole file.
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003221 uep = uhp->uh_entry;
3222 if (uep->ue_top != 0 || uep->ue_bot != 0)
3223 return;
3224
3225 for (lnum = 1; lnum < curbuf->b_ml.ml_line_count
3226 && lnum <= uep->ue_size; ++lnum)
Bram Moolenaarccae4672019-01-04 15:09:57 +01003227 {
3228 char_u *p = ml_get_buf(curbuf, lnum, FALSE);
3229
3230 if (uep->ue_array[lnum - 1].ul_len != curbuf->b_ml.ml_line_len
3231 || memcmp(p, uep->ue_array[lnum - 1].ul_line, uep->ue_array[lnum - 1].ul_len) != 0)
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003232 {
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01003233 CLEAR_POS(&(uhp->uh_cursor));
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003234 uhp->uh_cursor.lnum = lnum;
3235 return;
3236 }
Bram Moolenaarccae4672019-01-04 15:09:57 +01003237 }
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003238 if (curbuf->b_ml.ml_line_count != uep->ue_size)
3239 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003240 // lines added or deleted at the end, put the cursor there
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01003241 CLEAR_POS(&(uhp->uh_cursor));
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003242 uhp->uh_cursor.lnum = lnum;
3243 }
3244}
3245
3246/*
Bram Moolenaar730cde92010-06-27 05:18:54 +02003247 * Increase the write count, store it in the last undo header, what would be
3248 * used for "u".
3249 */
3250 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003251u_update_save_nr(buf_T *buf)
Bram Moolenaar730cde92010-06-27 05:18:54 +02003252{
3253 u_header_T *uhp;
3254
3255 ++buf->b_u_save_nr_last;
3256 buf->b_u_save_nr_cur = buf->b_u_save_nr_last;
3257 uhp = buf->b_u_curhead;
3258 if (uhp != NULL)
3259 uhp = uhp->uh_next.ptr;
3260 else
3261 uhp = buf->b_u_newhead;
3262 if (uhp != NULL)
3263 uhp->uh_save_nr = buf->b_u_save_nr_last;
3264}
3265
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003266 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003267u_unch_branch(u_header_T *uhp)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003268{
Bram Moolenaar1e607892006-03-13 22:15:53 +00003269 u_header_T *uh;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003270
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003271 for (uh = uhp; uh != NULL; uh = uh->uh_prev.ptr)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003272 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003273 uh->uh_flags |= UH_CHANGED;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003274 if (uh->uh_alt_next.ptr != NULL)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003275 u_unch_branch(uh->uh_alt_next.ptr); // recursive
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003276 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003277}
3278
3279/*
3280 * Get pointer to last added entry.
3281 * If it's not valid, give an error message and return NULL.
3282 */
3283 static u_entry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003284u_get_headentry(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003285{
3286 if (curbuf->b_u_newhead == NULL || curbuf->b_u_newhead->uh_entry == NULL)
3287 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003288 iemsg(_("E439: undo list corrupt"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003289 return NULL;
3290 }
3291 return curbuf->b_u_newhead->uh_entry;
3292}
3293
3294/*
3295 * u_getbot(): compute the line number of the previous u_save
3296 * It is called only when b_u_synced is FALSE.
3297 */
3298 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003299u_getbot(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003300{
3301 u_entry_T *uep;
3302 linenr_T extra;
3303
Bram Moolenaare38eab22019-12-05 21:50:01 +01003304 uep = u_get_headentry(); // check for corrupt undo list
Bram Moolenaar071d4272004-06-13 20:20:40 +00003305 if (uep == NULL)
3306 return;
3307
3308 uep = curbuf->b_u_newhead->uh_getbot_entry;
3309 if (uep != NULL)
3310 {
3311 /*
3312 * the new ue_bot is computed from the number of lines that has been
3313 * inserted (0 - deleted) since calling u_save. This is equal to the
3314 * old line count subtracted from the current line count.
3315 */
3316 extra = curbuf->b_ml.ml_line_count - uep->ue_lcount;
3317 uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra;
3318 if (uep->ue_bot < 1 || uep->ue_bot > curbuf->b_ml.ml_line_count)
3319 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003320 iemsg(_("E440: undo line missing"));
Bram Moolenaare38eab22019-12-05 21:50:01 +01003321 uep->ue_bot = uep->ue_top + 1; // assume all lines deleted, will
3322 // get all the old lines back
3323 // without deleting the current
3324 // ones
Bram Moolenaar071d4272004-06-13 20:20:40 +00003325 }
3326
3327 curbuf->b_u_newhead->uh_getbot_entry = NULL;
3328 }
3329
3330 curbuf->b_u_synced = TRUE;
3331}
3332
3333/*
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003334 * Free one header "uhp" and its entry list and adjust the pointers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003335 */
3336 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003337u_freeheader(
3338 buf_T *buf,
3339 u_header_T *uhp,
Bram Moolenaare38eab22019-12-05 21:50:01 +01003340 u_header_T **uhpp) // if not NULL reset when freeing this header
Bram Moolenaar071d4272004-06-13 20:20:40 +00003341{
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003342 u_header_T *uhap;
3343
Bram Moolenaare38eab22019-12-05 21:50:01 +01003344 // When there is an alternate redo list free that branch completely,
3345 // because we can never go there.
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003346 if (uhp->uh_alt_next.ptr != NULL)
3347 u_freebranch(buf, uhp->uh_alt_next.ptr, uhpp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003348
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003349 if (uhp->uh_alt_prev.ptr != NULL)
3350 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003351
Bram Moolenaare38eab22019-12-05 21:50:01 +01003352 // Update the links in the list to remove the header.
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003353 if (uhp->uh_next.ptr == NULL)
3354 buf->b_u_oldhead = uhp->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003355 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003356 uhp->uh_next.ptr->uh_prev.ptr = uhp->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003357
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003358 if (uhp->uh_prev.ptr == NULL)
3359 buf->b_u_newhead = uhp->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003360 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003361 for (uhap = uhp->uh_prev.ptr; uhap != NULL;
3362 uhap = uhap->uh_alt_next.ptr)
3363 uhap->uh_next.ptr = uhp->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003364
Bram Moolenaar1e607892006-03-13 22:15:53 +00003365 u_freeentries(buf, uhp, uhpp);
3366}
3367
3368/*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003369 * Free an alternate branch and any following alternate branches.
Bram Moolenaar1e607892006-03-13 22:15:53 +00003370 */
3371 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003372u_freebranch(
3373 buf_T *buf,
3374 u_header_T *uhp,
Bram Moolenaare38eab22019-12-05 21:50:01 +01003375 u_header_T **uhpp) // if not NULL reset when freeing this header
Bram Moolenaar1e607892006-03-13 22:15:53 +00003376{
3377 u_header_T *tofree, *next;
3378
Bram Moolenaare38eab22019-12-05 21:50:01 +01003379 // If this is the top branch we may need to use u_freeheader() to update
3380 // all the pointers.
Bram Moolenaar07d06772007-11-10 21:51:15 +00003381 if (uhp == buf->b_u_oldhead)
3382 {
Bram Moolenaaraa887322013-11-07 03:04:11 +01003383 while (buf->b_u_oldhead != NULL)
3384 u_freeheader(buf, buf->b_u_oldhead, uhpp);
Bram Moolenaar07d06772007-11-10 21:51:15 +00003385 return;
3386 }
3387
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003388 if (uhp->uh_alt_prev.ptr != NULL)
3389 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +00003390
3391 next = uhp;
3392 while (next != NULL)
3393 {
3394 tofree = next;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003395 if (tofree->uh_alt_next.ptr != NULL)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003396 u_freebranch(buf, tofree->uh_alt_next.ptr, uhpp); // recursive
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003397 next = tofree->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00003398 u_freeentries(buf, tofree, uhpp);
3399 }
3400}
3401
3402/*
3403 * Free all the undo entries for one header and the header itself.
3404 * This means that "uhp" is invalid when returning.
3405 */
3406 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003407u_freeentries(
3408 buf_T *buf,
3409 u_header_T *uhp,
Bram Moolenaare38eab22019-12-05 21:50:01 +01003410 u_header_T **uhpp) // if not NULL reset when freeing this header
Bram Moolenaar1e607892006-03-13 22:15:53 +00003411{
3412 u_entry_T *uep, *nuep;
3413
Bram Moolenaare38eab22019-12-05 21:50:01 +01003414 // Check for pointers to the header that become invalid now.
Bram Moolenaar1e607892006-03-13 22:15:53 +00003415 if (buf->b_u_curhead == uhp)
3416 buf->b_u_curhead = NULL;
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003417 if (buf->b_u_newhead == uhp)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003418 buf->b_u_newhead = NULL; // freeing the newest entry
Bram Moolenaar1e607892006-03-13 22:15:53 +00003419 if (uhpp != NULL && uhp == *uhpp)
3420 *uhpp = NULL;
3421
3422 for (uep = uhp->uh_entry; uep != NULL; uep = nuep)
3423 {
3424 nuep = uep->ue_next;
3425 u_freeentry(uep, uep->ue_size);
3426 }
3427
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003428#ifdef U_DEBUG
3429 uhp->uh_magic = 0;
3430#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003431 vim_free((char_u *)uhp);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00003432 --buf->b_u_numhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003433}
3434
3435/*
3436 * free entry 'uep' and 'n' lines in uep->ue_array[]
3437 */
3438 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003439u_freeentry(u_entry_T *uep, long n)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003440{
Bram Moolenaar8d343302005-07-12 22:46:17 +00003441 while (n > 0)
Bram Moolenaarccae4672019-01-04 15:09:57 +01003442 vim_free(uep->ue_array[--n].ul_line);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003443 vim_free((char_u *)uep->ue_array);
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003444#ifdef U_DEBUG
3445 uep->ue_magic = 0;
3446#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003447 vim_free((char_u *)uep);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003448}
3449
3450/*
3451 * invalidate the undo buffer; called when storage has already been released
3452 */
3453 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003454u_clearall(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003455{
3456 buf->b_u_newhead = buf->b_u_oldhead = buf->b_u_curhead = NULL;
3457 buf->b_u_synced = TRUE;
3458 buf->b_u_numhead = 0;
Bram Moolenaarccae4672019-01-04 15:09:57 +01003459 buf->b_u_line_ptr.ul_line = NULL;
3460 buf->b_u_line_ptr.ul_len = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003461 buf->b_u_line_lnum = 0;
3462}
3463
3464/*
Bram Moolenaarccae4672019-01-04 15:09:57 +01003465 * Save the line "lnum" for the "U" command.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003466 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02003467 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003468u_saveline(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003469{
Bram Moolenaare38eab22019-12-05 21:50:01 +01003470 if (lnum == curbuf->b_u_line_lnum) // line is already saved
Bram Moolenaar071d4272004-06-13 20:20:40 +00003471 return;
Bram Moolenaare38eab22019-12-05 21:50:01 +01003472 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) // should never happen
Bram Moolenaar071d4272004-06-13 20:20:40 +00003473 return;
3474 u_clearline();
3475 curbuf->b_u_line_lnum = lnum;
3476 if (curwin->w_cursor.lnum == lnum)
3477 curbuf->b_u_line_colnr = curwin->w_cursor.col;
3478 else
3479 curbuf->b_u_line_colnr = 0;
Bram Moolenaarccae4672019-01-04 15:09:57 +01003480 if (u_save_line(&curbuf->b_u_line_ptr, lnum) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003481 do_outofmem_msg((long_u)0);
3482}
3483
3484/*
3485 * clear the line saved for the "U" command
3486 * (this is used externally for crossing a line while in insert mode)
3487 */
3488 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003489u_clearline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003490{
Bram Moolenaarccae4672019-01-04 15:09:57 +01003491 if (curbuf->b_u_line_ptr.ul_line != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003492 {
Bram Moolenaarccae4672019-01-04 15:09:57 +01003493 VIM_CLEAR(curbuf->b_u_line_ptr.ul_line);
3494 curbuf->b_u_line_ptr.ul_len = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003495 curbuf->b_u_line_lnum = 0;
3496 }
3497}
3498
3499/*
3500 * Implementation of the "U" command.
3501 * Differentiation from vi: "U" can be undone with the next "U".
3502 * We also allow the cursor to be in another line.
Bram Moolenaard04b7502010-07-08 22:27:55 +02003503 * Careful: may trigger autocommands that reload the buffer.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003504 */
3505 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003506u_undoline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003507{
Bram Moolenaarccae4672019-01-04 15:09:57 +01003508 colnr_T t;
3509 undoline_T oldp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003510
3511 if (undo_off)
3512 return;
3513
Bram Moolenaarccae4672019-01-04 15:09:57 +01003514 if (curbuf->b_u_line_ptr.ul_line == NULL
Bram Moolenaare3300c82008-02-13 14:21:38 +00003515 || curbuf->b_u_line_lnum > curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003516 {
3517 beep_flush();
3518 return;
3519 }
Bram Moolenaare3300c82008-02-13 14:21:38 +00003520
Bram Moolenaarccae4672019-01-04 15:09:57 +01003521 // first save the line for the 'u' command
Bram Moolenaar071d4272004-06-13 20:20:40 +00003522 if (u_savecommon(curbuf->b_u_line_lnum - 1,
Bram Moolenaar59f931e2010-07-24 20:27:03 +02003523 curbuf->b_u_line_lnum + 1, (linenr_T)0, FALSE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003524 return;
Bram Moolenaarccae4672019-01-04 15:09:57 +01003525 if (u_save_line(&oldp, curbuf->b_u_line_lnum) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003526 {
3527 do_outofmem_msg((long_u)0);
3528 return;
3529 }
Bram Moolenaar02c037a2020-08-30 19:26:45 +02003530 ml_replace_len(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr.ul_line,
3531 curbuf->b_u_line_ptr.ul_len, TRUE, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003532 changed_bytes(curbuf->b_u_line_lnum, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003533 curbuf->b_u_line_ptr = oldp;
3534
3535 t = curbuf->b_u_line_colnr;
3536 if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum)
3537 curbuf->b_u_line_colnr = curwin->w_cursor.col;
3538 curwin->w_cursor.col = t;
3539 curwin->w_cursor.lnum = curbuf->b_u_line_lnum;
Bram Moolenaare3300c82008-02-13 14:21:38 +00003540 check_cursor_col();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003541}
3542
3543/*
Bram Moolenaar26a60b42005-02-22 08:49:11 +00003544 * Free all allocated memory blocks for the buffer 'buf'.
3545 */
3546 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003547u_blockfree(buf_T *buf)
Bram Moolenaar26a60b42005-02-22 08:49:11 +00003548{
Bram Moolenaar1e607892006-03-13 22:15:53 +00003549 while (buf->b_u_oldhead != NULL)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003550 u_freeheader(buf, buf->b_u_oldhead, NULL);
Bram Moolenaarccae4672019-01-04 15:09:57 +01003551 vim_free(buf->b_u_line_ptr.ul_line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003552}
3553
3554/*
3555 * Check if the 'modified' flag is set, or 'ff' has changed (only need to
3556 * check the first character, because it can only be "dos", "unix" or "mac").
3557 * "nofile" and "scratch" type buffers are considered to always be unchanged.
Bram Moolenaarf405c8f2017-12-09 19:51:49 +01003558 * Also considers a buffer changed when a terminal window contains a running
3559 * job.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003560 */
3561 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003562bufIsChanged(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003563{
Bram Moolenaareb44a682017-08-03 22:44:55 +02003564#ifdef FEAT_TERMINAL
3565 if (term_job_running(buf->b_term))
3566 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003567#endif
Bram Moolenaarf405c8f2017-12-09 19:51:49 +01003568 return bufIsChangedNotTerm(buf);
3569}
3570
3571/*
Bram Moolenaara84a3dd2019-03-25 22:21:24 +01003572 * Return TRUE if any buffer has changes. Also buffers that are not written.
3573 */
3574 int
3575anyBufIsChanged(void)
3576{
3577 buf_T *buf;
3578
3579 FOR_ALL_BUFFERS(buf)
3580 if (bufIsChanged(buf))
3581 return TRUE;
Bram Moolenaard6c3f1f2019-03-26 00:31:21 +01003582 return FALSE;
Bram Moolenaara84a3dd2019-03-25 22:21:24 +01003583}
3584
3585/*
Bram Moolenaarf405c8f2017-12-09 19:51:49 +01003586 * Like bufIsChanged() but ignoring a terminal window.
3587 */
3588 int
3589bufIsChangedNotTerm(buf_T *buf)
3590{
Bram Moolenaar4551c0a2018-06-20 22:38:21 +02003591 // In a "prompt" buffer we do respect 'modified', so that we can control
3592 // closing the window by setting or resetting that option.
3593 return (!bt_dontwrite(buf) || bt_prompt(buf))
Bram Moolenaareb44a682017-08-03 22:44:55 +02003594 && (buf->b_changed || file_ff_differs(buf, TRUE));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003595}
3596
3597 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003598curbufIsChanged(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003599{
Bram Moolenaareb44a682017-08-03 22:44:55 +02003600 return bufIsChanged(curbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003601}
Bram Moolenaara800b422010-06-27 01:15:55 +02003602
3603#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003604
Bram Moolenaara800b422010-06-27 01:15:55 +02003605/*
3606 * For undotree(): Append the list of undo blocks at "first_uhp" to "list".
3607 * Recursive.
3608 */
Bram Moolenaar840d16f2019-09-10 21:27:18 +02003609 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003610u_eval_tree(u_header_T *first_uhp, list_T *list)
Bram Moolenaara800b422010-06-27 01:15:55 +02003611{
3612 u_header_T *uhp = first_uhp;
3613 dict_T *dict;
3614
3615 while (uhp != NULL)
3616 {
3617 dict = dict_alloc();
3618 if (dict == NULL)
3619 return;
Bram Moolenaare0be1672018-07-08 16:50:37 +02003620 dict_add_number(dict, "seq", uhp->uh_seq);
3621 dict_add_number(dict, "time", (long)uhp->uh_time);
Bram Moolenaara800b422010-06-27 01:15:55 +02003622 if (uhp == curbuf->b_u_newhead)
Bram Moolenaare0be1672018-07-08 16:50:37 +02003623 dict_add_number(dict, "newhead", 1);
Bram Moolenaara800b422010-06-27 01:15:55 +02003624 if (uhp == curbuf->b_u_curhead)
Bram Moolenaare0be1672018-07-08 16:50:37 +02003625 dict_add_number(dict, "curhead", 1);
Bram Moolenaara800b422010-06-27 01:15:55 +02003626 if (uhp->uh_save_nr > 0)
Bram Moolenaare0be1672018-07-08 16:50:37 +02003627 dict_add_number(dict, "save", uhp->uh_save_nr);
Bram Moolenaara800b422010-06-27 01:15:55 +02003628
3629 if (uhp->uh_alt_next.ptr != NULL)
3630 {
3631 list_T *alt_list = list_alloc();
3632
3633 if (alt_list != NULL)
3634 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003635 // Recursive call to add alternate undo tree.
Bram Moolenaara800b422010-06-27 01:15:55 +02003636 u_eval_tree(uhp->uh_alt_next.ptr, alt_list);
3637 dict_add_list(dict, "alt", alt_list);
3638 }
3639 }
3640
3641 list_append_dict(list, dict);
3642 uhp = uhp->uh_prev.ptr;
3643 }
3644}
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003645
3646/*
3647 * "undofile(name)" function
3648 */
3649 void
3650f_undofile(typval_T *argvars UNUSED, typval_T *rettv)
3651{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02003652 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
3653 return;
3654
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003655 rettv->v_type = VAR_STRING;
3656#ifdef FEAT_PERSISTENT_UNDO
3657 {
3658 char_u *fname = tv_get_string(&argvars[0]);
3659
3660 if (*fname == NUL)
3661 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003662 // If there is no file name there will be no undo file.
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003663 rettv->vval.v_string = NULL;
3664 }
3665 else
3666 {
3667 char_u *ffname = FullName_save(fname, TRUE);
3668
3669 if (ffname != NULL)
3670 rettv->vval.v_string = u_get_undo_file_name(ffname, FALSE);
3671 vim_free(ffname);
3672 }
3673 }
3674#else
3675 rettv->vval.v_string = NULL;
3676#endif
3677}
Christian Brabandt8a4c8122021-07-25 14:36:05 +02003678#ifdef FEAT_PERSISTENT_UNDO
3679/*
3680 * Reset undofile option and delete the undofile
3681 */
3682 void
3683u_undofile_reset_and_delete(buf_T *buf)
3684{
3685 char_u *file_name;
3686
3687 if (!buf->b_p_udf)
3688 return;
3689
3690 file_name = u_get_undo_file_name(buf->b_ffname, TRUE);
3691 if (file_name != NULL)
3692 {
3693 mch_remove(file_name);
3694 vim_free(file_name);
3695 }
3696
3697 set_option_value((char_u *)"undofile", 0L, NULL, OPT_LOCAL);
3698}
3699 #endif
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003700
3701/*
3702 * "undotree()" function
3703 */
3704 void
3705f_undotree(typval_T *argvars UNUSED, typval_T *rettv)
3706{
3707 if (rettv_dict_alloc(rettv) == OK)
3708 {
3709 dict_T *dict = rettv->vval.v_dict;
3710 list_T *list;
3711
3712 dict_add_number(dict, "synced", (long)curbuf->b_u_synced);
3713 dict_add_number(dict, "seq_last", curbuf->b_u_seq_last);
3714 dict_add_number(dict, "save_last", (long)curbuf->b_u_save_nr_last);
3715 dict_add_number(dict, "seq_cur", curbuf->b_u_seq_cur);
3716 dict_add_number(dict, "time_cur", (long)curbuf->b_u_time_cur);
3717 dict_add_number(dict, "save_cur", (long)curbuf->b_u_save_nr_cur);
3718
3719 list = list_alloc();
3720 if (list != NULL)
3721 {
3722 u_eval_tree(curbuf->b_u_oldhead, list);
3723 dict_add_list(dict, "entries", list);
3724 }
3725 }
3726}
3727
Bram Moolenaara800b422010-06-27 01:15:55 +02003728#endif