blob: 1cd8912823a0d1efad42797e712507534a76e3cd [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);
Christian Brabandt9071ed82024-02-15 20:17:37 +0100126static void u_blockfree(buf_T *buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000127
Bram Moolenaar18a4ba22019-05-24 19:39:03 +0200128#define U_ALLOC_LINE(size) lalloc(size, FALSE)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000129
Bram Moolenaare38eab22019-12-05 21:50:01 +0100130// used in undo_end() to report number of added and deleted lines
Bram Moolenaar071d4272004-06-13 20:20:40 +0000131static long u_newcount, u_oldcount;
132
133/*
134 * When 'u' flag included in 'cpoptions', we behave like vi. Need to remember
135 * the action that "u" should do.
136 */
137static int undo_undoes = FALSE;
138
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200139static int lastmark = 0;
140
Bram Moolenaar9db58062010-05-29 20:33:07 +0200141#if defined(U_DEBUG) || defined(PROTO)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000142/*
143 * Check the undo structures for being valid. Print a warning when something
144 * looks wrong.
145 */
146static int seen_b_u_curhead;
147static int seen_b_u_newhead;
148static int header_count;
149
150 static void
151u_check_tree(u_header_T *uhp,
152 u_header_T *exp_uh_next,
153 u_header_T *exp_uh_alt_prev)
154{
155 u_entry_T *uep;
156
157 if (uhp == NULL)
158 return;
159 ++header_count;
160 if (uhp == curbuf->b_u_curhead && ++seen_b_u_curhead > 1)
161 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100162 emsg("b_u_curhead found twice (looping?)");
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000163 return;
164 }
165 if (uhp == curbuf->b_u_newhead && ++seen_b_u_newhead > 1)
166 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100167 emsg("b_u_newhead found twice (looping?)");
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000168 return;
169 }
170
171 if (uhp->uh_magic != UH_MAGIC)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100172 emsg("uh_magic wrong (may be using freed memory)");
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000173 else
174 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100175 // Check pointers back are correct.
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200176 if (uhp->uh_next.ptr != exp_uh_next)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000177 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100178 emsg("uh_next wrong");
179 smsg("expected: 0x%x, actual: 0x%x",
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200180 exp_uh_next, uhp->uh_next.ptr);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000181 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200182 if (uhp->uh_alt_prev.ptr != exp_uh_alt_prev)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000183 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100184 emsg("uh_alt_prev wrong");
185 smsg("expected: 0x%x, actual: 0x%x",
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200186 exp_uh_alt_prev, uhp->uh_alt_prev.ptr);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000187 }
188
Bram Moolenaare38eab22019-12-05 21:50:01 +0100189 // Check the undo tree at this header.
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000190 for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
191 {
192 if (uep->ue_magic != UE_MAGIC)
193 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100194 emsg("ue_magic wrong (may be using freed memory)");
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000195 break;
196 }
197 }
198
Bram Moolenaare38eab22019-12-05 21:50:01 +0100199 // Check the next alt tree.
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200200 u_check_tree(uhp->uh_alt_next.ptr, uhp->uh_next.ptr, uhp);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000201
Bram Moolenaare38eab22019-12-05 21:50:01 +0100202 // Check the next header in this branch.
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200203 u_check_tree(uhp->uh_prev.ptr, uhp, NULL);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000204 }
205}
206
Bram Moolenaarb0b50882010-07-07 18:26:28 +0200207 static void
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000208u_check(int newhead_may_be_NULL)
209{
210 seen_b_u_newhead = 0;
211 seen_b_u_curhead = 0;
212 header_count = 0;
213
214 u_check_tree(curbuf->b_u_oldhead, NULL, NULL);
215
216 if (seen_b_u_newhead == 0 && curbuf->b_u_oldhead != NULL
217 && !(newhead_may_be_NULL && curbuf->b_u_newhead == NULL))
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100218 semsg("b_u_newhead invalid: 0x%x", curbuf->b_u_newhead);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000219 if (curbuf->b_u_curhead != NULL && seen_b_u_curhead == 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100220 semsg("b_u_curhead invalid: 0x%x", curbuf->b_u_curhead);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000221 if (header_count != curbuf->b_u_numhead)
222 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100223 emsg("b_u_numhead invalid");
224 smsg("expected: %ld, actual: %ld",
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000225 (long)header_count, (long)curbuf->b_u_numhead);
226 }
227}
228#endif
229
Bram Moolenaar071d4272004-06-13 20:20:40 +0000230/*
Bram Moolenaard857f0e2005-06-21 22:37:39 +0000231 * Save the current line for both the "u" and "U" command.
Bram Moolenaar687a29c2013-04-15 15:47:12 +0200232 * Careful: may trigger autocommands that reload the buffer.
Bram Moolenaard857f0e2005-06-21 22:37:39 +0000233 * Returns OK or FAIL.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000234 */
235 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100236u_save_cursor(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000237{
238 return (u_save((linenr_T)(curwin->w_cursor.lnum - 1),
239 (linenr_T)(curwin->w_cursor.lnum + 1)));
240}
241
242/*
243 * Save the lines between "top" and "bot" for both the "u" and "U" command.
Bram Moolenaar739f13a2021-12-13 13:12:53 +0000244 * "top" may be 0 and "bot" may be curbuf->b_ml.ml_line_count + 1.
Bram Moolenaard04b7502010-07-08 22:27:55 +0200245 * Careful: may trigger autocommands that reload the buffer.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000246 * Returns FAIL when lines could not be saved, OK otherwise.
247 */
248 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100249u_save(linenr_T top, linenr_T bot)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000250{
251 if (undo_off)
252 return OK;
253
Bram Moolenaarefc81332018-07-13 16:31:19 +0200254 if (top >= bot || bot > curbuf->b_ml.ml_line_count + 1)
255 return FAIL; // rely on caller to give an error message
Bram Moolenaar071d4272004-06-13 20:20:40 +0000256
257 if (top + 2 == bot)
258 u_saveline((linenr_T)(top + 1));
259
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200260 return (u_savecommon(top, bot, (linenr_T)0, FALSE));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000261}
262
263/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200264 * Save the line "lnum" (used by ":s" and "~" command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000265 * The line is replaced, so the new bottom line is lnum + 1.
Bram Moolenaard04b7502010-07-08 22:27:55 +0200266 * Careful: may trigger autocommands that reload the buffer.
267 * Returns FAIL when lines could not be saved, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000268 */
269 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100270u_savesub(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000271{
272 if (undo_off)
273 return OK;
274
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200275 return (u_savecommon(lnum - 1, lnum + 1, lnum + 1, FALSE));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000276}
277
278/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200279 * A new line is inserted before line "lnum" (used by :s command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000280 * The line is inserted, so the new bottom line is lnum + 1.
Bram Moolenaard04b7502010-07-08 22:27:55 +0200281 * Careful: may trigger autocommands that reload the buffer.
282 * Returns FAIL when lines could not be saved, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000283 */
284 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100285u_inssub(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000286{
287 if (undo_off)
288 return OK;
289
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200290 return (u_savecommon(lnum - 1, lnum, lnum + 1, FALSE));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000291}
292
293/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200294 * Save the lines "lnum" - "lnum" + nlines (used by delete command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000295 * The lines are deleted, so the new bottom line is lnum, unless the buffer
296 * becomes empty.
Bram Moolenaard04b7502010-07-08 22:27:55 +0200297 * Careful: may trigger autocommands that reload the buffer.
298 * Returns FAIL when lines could not be saved, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000299 */
300 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100301u_savedel(linenr_T lnum, long nlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000302{
303 if (undo_off)
304 return OK;
305
306 return (u_savecommon(lnum - 1, lnum + nlines,
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200307 nlines == curbuf->b_ml.ml_line_count ? 2 : lnum, FALSE));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000308}
309
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000310/*
311 * Return TRUE when undo is allowed. Otherwise give an error message and
312 * return FALSE.
313 */
Bram Moolenaarce6ef252006-07-12 19:49:41 +0000314 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100315undo_allowed(void)
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000316{
Bram Moolenaare38eab22019-12-05 21:50:01 +0100317 // Don't allow changes when 'modifiable' is off.
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000318 if (!curbuf->b_p_ma)
319 {
Bram Moolenaar108010a2021-06-27 22:03:33 +0200320 emsg(_(e_cannot_make_changes_modifiable_is_off));
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000321 return FALSE;
322 }
323
324#ifdef HAVE_SANDBOX
Bram Moolenaare38eab22019-12-05 21:50:01 +0100325 // In the sandbox it's not allowed to change the text.
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000326 if (sandbox != 0)
327 {
Bram Moolenaard8e44472021-07-21 22:20:33 +0200328 emsg(_(e_not_allowed_in_sandbox));
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000329 return FALSE;
330 }
331#endif
332
Bram Moolenaare38eab22019-12-05 21:50:01 +0100333 // Don't allow changes in the buffer while editing the cmdline. The
334 // caller of getcmdline() may get confused.
zeertzjqcfe45652022-05-27 17:26:55 +0100335 if (textlock != 0)
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000336 {
zeertzjqcfe45652022-05-27 17:26:55 +0100337 emsg(_(e_not_allowed_to_change_text_or_change_window));
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000338 return FALSE;
339 }
340
341 return TRUE;
342}
343
Bram Moolenaarb0b50882010-07-07 18:26:28 +0200344/*
Bram Moolenaar32aa1022019-11-02 22:54:41 +0100345 * Get the undolevel value for the current buffer.
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +0100346 */
347 static long
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100348get_undolevel(void)
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +0100349{
350 if (curbuf->b_p_ul == NO_LOCAL_UNDOLEVEL)
351 return p_ul;
352 return curbuf->b_p_ul;
353}
354
355/*
Bram Moolenaarccae4672019-01-04 15:09:57 +0100356 * u_save_line(): save an allocated copy of line "lnum" into "ul".
357 * Returns FAIL when out of memory.
358 */
359 static int
360u_save_line(undoline_T *ul, linenr_T lnum)
361{
362 char_u *line = ml_get(lnum);
363
364 if (curbuf->b_ml.ml_line_len == 0)
365 {
366 ul->ul_len = 1;
367 ul->ul_line = vim_strsave((char_u *)"");
368 }
369 else
370 {
Bram Moolenaar964b3742019-05-24 18:54:09 +0200371 // This uses the length in the memline, thus text properties are
372 // included.
Bram Moolenaarccae4672019-01-04 15:09:57 +0100373 ul->ul_len = curbuf->b_ml.ml_line_len;
374 ul->ul_line = vim_memsave(line, ul->ul_len);
375 }
376 return ul->ul_line == NULL ? FAIL : OK;
377}
378
Bram Moolenaard5c2c772020-05-30 16:17:33 +0200379#ifdef FEAT_PROP_POPUP
Bram Moolenaarccae4672019-01-04 15:09:57 +0100380/*
Bram Moolenaara9d4b842020-05-30 14:46:52 +0200381 * return TRUE if line "lnum" has text property "flags".
382 */
383 static int
384has_prop_w_flags(linenr_T lnum, int flags)
385{
386 char_u *props;
387 int i;
388 int proplen = get_text_props(curbuf, lnum, &props, FALSE);
389
390 for (i = 0; i < proplen; ++i)
391 {
392 textprop_T prop;
393
394 mch_memmove(&prop, props + i * sizeof prop, sizeof prop);
395 if (prop.tp_flags & flags)
396 return TRUE;
397 }
398 return FALSE;
399}
Bram Moolenaard5c2c772020-05-30 16:17:33 +0200400#endif
Bram Moolenaara9d4b842020-05-30 14:46:52 +0200401
402/*
Bram Moolenaarb0b50882010-07-07 18:26:28 +0200403 * Common code for various ways to save text before a change.
Bram Moolenaard04b7502010-07-08 22:27:55 +0200404 * "top" is the line above the first changed line.
405 * "bot" is the line below the last changed line.
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200406 * "newbot" is the new bottom line. Use zero when not known.
407 * "reload" is TRUE when saving for a buffer reload.
Bram Moolenaard04b7502010-07-08 22:27:55 +0200408 * Careful: may trigger autocommands that reload the buffer.
409 * Returns FAIL when lines could not be saved, OK otherwise.
Bram Moolenaarb0b50882010-07-07 18:26:28 +0200410 */
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200411 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100412u_savecommon(
413 linenr_T top,
414 linenr_T bot,
415 linenr_T newbot,
416 int reload)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000417{
Bram Moolenaar1e607892006-03-13 22:15:53 +0000418 linenr_T lnum;
419 long i;
420 u_header_T *uhp;
421 u_header_T *old_curhead;
422 u_entry_T *uep;
423 u_entry_T *prev_uep;
424 long size;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000425
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200426 if (!reload)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000427 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100428 // When making changes is not allowed return FAIL. It's a crude way
429 // to make all change commands fail.
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200430 if (!undo_allowed())
Bram Moolenaar009b2592004-10-24 19:18:58 +0000431 return FAIL;
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200432
433#ifdef FEAT_NETBEANS_INTG
434 /*
435 * Netbeans defines areas that cannot be modified. Bail out here when
436 * trying to change text in a guarded area.
437 */
438 if (netbeans_active())
Bram Moolenaar009b2592004-10-24 19:18:58 +0000439 {
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200440 if (netbeans_is_guarded(top, bot))
441 {
Bram Moolenaar74409f62022-01-01 15:58:22 +0000442 emsg(_(e_region_is_guarded_cannot_modify));
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200443 return FAIL;
444 }
445 if (curbuf->b_p_ro)
446 {
Bram Moolenaar74409f62022-01-01 15:58:22 +0000447 emsg(_(e_netbeans_does_not_allow_changes_in_read_only_files));
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200448 return FAIL;
449 }
Bram Moolenaar009b2592004-10-24 19:18:58 +0000450 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000451#endif
Bram Moolenaar63ecdda2017-07-28 22:29:35 +0200452#ifdef FEAT_TERMINAL
Bram Moolenaare38eab22019-12-05 21:50:01 +0100453 // A change in a terminal buffer removes the highlighting.
Bram Moolenaar63ecdda2017-07-28 22:29:35 +0200454 term_change_in_curbuf();
455#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000456
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200457 /*
458 * Saving text for undo means we are going to make a change. Give a
459 * warning for a read-only file before making the change, so that the
460 * FileChangedRO event can replace the buffer with a read-write version
461 * (e.g., obtained from a source control system).
462 */
463 change_warning(0);
464 if (bot > curbuf->b_ml.ml_line_count + 1)
465 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100466 // This happens when the FileChangedRO autocommand changes the
467 // file in a way it becomes shorter.
Bram Moolenaard82a47d2022-01-05 20:24:39 +0000468 emsg(_(e_line_count_changed_unexpectedly));
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200469 return FAIL;
470 }
Bram Moolenaard04b7502010-07-08 22:27:55 +0200471 }
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200472
473#ifdef U_DEBUG
474 u_check(FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000475#endif
476
Bram Moolenaara9d4b842020-05-30 14:46:52 +0200477#ifdef FEAT_PROP_POPUP
478 // Include the line above if a text property continues from it.
479 // Include the line below if a text property continues to it.
480 if (bot - top > 1)
481 {
482 if (top > 0 && has_prop_w_flags(top + 1, TP_FLAG_CONT_PREV))
483 --top;
484 if (bot <= curbuf->b_ml.ml_line_count
485 && has_prop_w_flags(bot - 1, TP_FLAG_CONT_NEXT))
486 {
487 ++bot;
488 if (newbot != 0)
489 ++newbot;
490 }
491 }
492#endif
493
Bram Moolenaar071d4272004-06-13 20:20:40 +0000494 size = bot - top - 1;
495
496 /*
Bram Moolenaarb0b50882010-07-07 18:26:28 +0200497 * If curbuf->b_u_synced == TRUE make a new header.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000498 */
499 if (curbuf->b_u_synced)
500 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100501 // Need to create new entry in b_changelist.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000502 curbuf->b_new_change = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000503
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +0100504 if (get_undolevel() >= 0)
Bram Moolenaar1e607892006-03-13 22:15:53 +0000505 {
506 /*
507 * Make a new header entry. Do this first so that we don't mess
508 * up the undo info when out of memory.
509 */
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200510 uhp = U_ALLOC_LINE(sizeof(u_header_T));
Bram Moolenaar1e607892006-03-13 22:15:53 +0000511 if (uhp == NULL)
512 goto nomem;
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000513#ifdef U_DEBUG
514 uhp->uh_magic = UH_MAGIC;
515#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +0000516 }
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000517 else
518 uhp = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000519
Bram Moolenaar071d4272004-06-13 20:20:40 +0000520 /*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000521 * If we undid more than we redid, move the entry lists before and
522 * including curbuf->b_u_curhead to an alternate branch.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000523 */
Bram Moolenaar1e607892006-03-13 22:15:53 +0000524 old_curhead = curbuf->b_u_curhead;
525 if (old_curhead != NULL)
526 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200527 curbuf->b_u_newhead = old_curhead->uh_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000528 curbuf->b_u_curhead = NULL;
529 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000530
531 /*
532 * free headers to keep the size right
533 */
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +0100534 while (curbuf->b_u_numhead > get_undolevel()
535 && curbuf->b_u_oldhead != NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +0000536 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000537 u_header_T *uhfree = curbuf->b_u_oldhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000538
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000539 if (uhfree == old_curhead)
Bram Moolenaare38eab22019-12-05 21:50:01 +0100540 // Can't reconnect the branch, delete all of it.
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000541 u_freebranch(curbuf, uhfree, &old_curhead);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200542 else if (uhfree->uh_alt_next.ptr == NULL)
Bram Moolenaare38eab22019-12-05 21:50:01 +0100543 // There is no branch, only free one header.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000544 u_freeheader(curbuf, uhfree, &old_curhead);
Bram Moolenaar1e607892006-03-13 22:15:53 +0000545 else
546 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100547 // Free the oldest alternate branch as a whole.
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200548 while (uhfree->uh_alt_next.ptr != NULL)
549 uhfree = uhfree->uh_alt_next.ptr;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000550 u_freebranch(curbuf, uhfree, &old_curhead);
Bram Moolenaar1e607892006-03-13 22:15:53 +0000551 }
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000552#ifdef U_DEBUG
553 u_check(TRUE);
554#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +0000555 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000556
Bram Moolenaare38eab22019-12-05 21:50:01 +0100557 if (uhp == NULL) // no undo at all
Bram Moolenaar071d4272004-06-13 20:20:40 +0000558 {
Bram Moolenaar1e607892006-03-13 22:15:53 +0000559 if (old_curhead != NULL)
560 u_freebranch(curbuf, old_curhead, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000561 curbuf->b_u_synced = FALSE;
562 return OK;
563 }
564
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200565 uhp->uh_prev.ptr = NULL;
566 uhp->uh_next.ptr = curbuf->b_u_newhead;
567 uhp->uh_alt_next.ptr = old_curhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000568 if (old_curhead != NULL)
569 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200570 uhp->uh_alt_prev.ptr = old_curhead->uh_alt_prev.ptr;
571 if (uhp->uh_alt_prev.ptr != NULL)
572 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = uhp;
573 old_curhead->uh_alt_prev.ptr = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000574 if (curbuf->b_u_oldhead == old_curhead)
575 curbuf->b_u_oldhead = uhp;
576 }
Bram Moolenaar89ed3df2007-01-09 19:23:12 +0000577 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200578 uhp->uh_alt_prev.ptr = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000579 if (curbuf->b_u_newhead != NULL)
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200580 curbuf->b_u_newhead->uh_prev.ptr = uhp;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000581
Bram Moolenaarca003e12006-03-17 23:19:38 +0000582 uhp->uh_seq = ++curbuf->b_u_seq_last;
583 curbuf->b_u_seq_cur = uhp->uh_seq;
Bram Moolenaar170b10b2016-07-29 16:15:27 +0200584 uhp->uh_time = vim_time();
Bram Moolenaara800b422010-06-27 01:15:55 +0200585 uhp->uh_save_nr = 0;
586 curbuf->b_u_time_cur = uhp->uh_time + 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000587
Bram Moolenaar1e607892006-03-13 22:15:53 +0000588 uhp->uh_walk = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000589 uhp->uh_entry = NULL;
590 uhp->uh_getbot_entry = NULL;
Bram Moolenaare38eab22019-12-05 21:50:01 +0100591 uhp->uh_cursor = curwin->w_cursor; // save cursor pos. for undo
Bram Moolenaar071d4272004-06-13 20:20:40 +0000592 if (virtual_active() && curwin->w_cursor.coladd > 0)
593 uhp->uh_cursor_vcol = getviscol();
594 else
595 uhp->uh_cursor_vcol = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000596
Bram Moolenaare38eab22019-12-05 21:50:01 +0100597 // save changed and buffer empty flag for undo
Bram Moolenaar071d4272004-06-13 20:20:40 +0000598 uhp->uh_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
599 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
600
Bram Moolenaare38eab22019-12-05 21:50:01 +0100601 // save named marks and Visual marks for undo
Bram Moolenaar071d4272004-06-13 20:20:40 +0000602 mch_memmove(uhp->uh_namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000603 uhp->uh_visual = curbuf->b_visual;
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000604
Bram Moolenaar071d4272004-06-13 20:20:40 +0000605 curbuf->b_u_newhead = uhp;
606 if (curbuf->b_u_oldhead == NULL)
607 curbuf->b_u_oldhead = uhp;
608 ++curbuf->b_u_numhead;
609 }
610 else
611 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100612 if (get_undolevel() < 0) // no undo at all
Bram Moolenaar071d4272004-06-13 20:20:40 +0000613 return OK;
614
615 /*
616 * When saving a single line, and it has been saved just before, it
617 * doesn't make sense saving it again. Saves a lot of memory when
618 * making lots of changes inside the same line.
619 * This is only possible if the previous change didn't increase or
620 * decrease the number of lines.
621 * Check the ten last changes. More doesn't make sense and takes too
622 * long.
623 */
624 if (size == 1)
625 {
626 uep = u_get_headentry();
627 prev_uep = NULL;
628 for (i = 0; i < 10; ++i)
629 {
630 if (uep == NULL)
631 break;
632
Bram Moolenaare38eab22019-12-05 21:50:01 +0100633 // If lines have been inserted/deleted we give up.
634 // Also when the line was included in a multi-line save.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000635 if ((curbuf->b_u_newhead->uh_getbot_entry != uep
636 ? (uep->ue_top + uep->ue_size + 1
637 != (uep->ue_bot == 0
638 ? curbuf->b_ml.ml_line_count + 1
639 : uep->ue_bot))
640 : uep->ue_lcount != curbuf->b_ml.ml_line_count)
641 || (uep->ue_size > 1
642 && top >= uep->ue_top
643 && top + 2 <= uep->ue_top + uep->ue_size + 1))
644 break;
645
Bram Moolenaare38eab22019-12-05 21:50:01 +0100646 // If it's the same line we can skip saving it again.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000647 if (uep->ue_size == 1 && uep->ue_top == top)
648 {
649 if (i > 0)
650 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100651 // It's not the last entry: get ue_bot for the last
652 // entry now. Following deleted/inserted lines go to
653 // the re-used entry.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000654 u_getbot();
655 curbuf->b_u_synced = FALSE;
656
Bram Moolenaare38eab22019-12-05 21:50:01 +0100657 // Move the found entry to become the last entry. The
658 // order of undo/redo doesn't matter for the entries
659 // we move it over, since they don't change the line
660 // count and don't include this line. It does matter
661 // for the found entry if the line count is changed by
662 // the executed command.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000663 prev_uep->ue_next = uep->ue_next;
664 uep->ue_next = curbuf->b_u_newhead->uh_entry;
665 curbuf->b_u_newhead->uh_entry = uep;
666 }
667
Bram Moolenaare38eab22019-12-05 21:50:01 +0100668 // The executed command may change the line count.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000669 if (newbot != 0)
670 uep->ue_bot = newbot;
671 else if (bot > curbuf->b_ml.ml_line_count)
672 uep->ue_bot = 0;
673 else
674 {
675 uep->ue_lcount = curbuf->b_ml.ml_line_count;
676 curbuf->b_u_newhead->uh_getbot_entry = uep;
677 }
678 return OK;
679 }
680 prev_uep = uep;
681 uep = uep->ue_next;
682 }
683 }
684
Bram Moolenaare38eab22019-12-05 21:50:01 +0100685 // find line number for ue_bot for previous u_save()
Bram Moolenaar071d4272004-06-13 20:20:40 +0000686 u_getbot();
687 }
688
Bram Moolenaar4f974752019-02-17 17:44:42 +0100689#if !defined(UNIX) && !defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000690 /*
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100691 * With Amiga we can't handle big undo's, because
Bram Moolenaar071d4272004-06-13 20:20:40 +0000692 * then u_alloc_line would have to allocate a block larger than 32K
693 */
694 if (size >= 8000)
695 goto nomem;
696#endif
697
698 /*
699 * add lines in front of entry list
700 */
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200701 uep = U_ALLOC_LINE(sizeof(u_entry_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000702 if (uep == NULL)
703 goto nomem;
Bram Moolenaara80faa82020-04-12 19:37:17 +0200704 CLEAR_POINTER(uep);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000705#ifdef U_DEBUG
706 uep->ue_magic = UE_MAGIC;
707#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000708
709 uep->ue_size = size;
710 uep->ue_top = top;
711 if (newbot != 0)
712 uep->ue_bot = newbot;
713 /*
714 * Use 0 for ue_bot if bot is below last line.
715 * Otherwise we have to compute ue_bot later.
716 */
717 else if (bot > curbuf->b_ml.ml_line_count)
718 uep->ue_bot = 0;
719 else
720 {
721 uep->ue_lcount = curbuf->b_ml.ml_line_count;
722 curbuf->b_u_newhead->uh_getbot_entry = uep;
723 }
724
Bram Moolenaar26a60b42005-02-22 08:49:11 +0000725 if (size > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000726 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200727 if ((uep->ue_array = U_ALLOC_LINE(sizeof(undoline_T) * size)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000728 {
729 u_freeentry(uep, 0L);
730 goto nomem;
731 }
732 for (i = 0, lnum = top + 1; i < size; ++i)
733 {
Bram Moolenaarae5bce12005-08-15 21:41:48 +0000734 fast_breakcheck();
735 if (got_int)
736 {
737 u_freeentry(uep, i);
738 return FAIL;
739 }
Bram Moolenaarccae4672019-01-04 15:09:57 +0100740 if (u_save_line(&uep->ue_array[i], lnum++) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000741 {
742 u_freeentry(uep, i);
743 goto nomem;
744 }
745 }
746 }
Bram Moolenaarf461c8e2005-06-25 23:04:51 +0000747 else
748 uep->ue_array = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000749 uep->ue_next = curbuf->b_u_newhead->uh_entry;
750 curbuf->b_u_newhead->uh_entry = uep;
751 curbuf->b_u_synced = FALSE;
752 undo_undoes = FALSE;
753
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000754#ifdef U_DEBUG
755 u_check(FALSE);
756#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000757 return OK;
758
759nomem:
Bram Moolenaare38eab22019-12-05 21:50:01 +0100760 msg_silent = 0; // must display the prompt
Bram Moolenaar071d4272004-06-13 20:20:40 +0000761 if (ask_yesno((char_u *)_("No undo possible; continue anyway"), TRUE)
762 == 'y')
763 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100764 undo_off = TRUE; // will be reset when character typed
Bram Moolenaar071d4272004-06-13 20:20:40 +0000765 return OK;
766 }
767 do_outofmem_msg((long_u)0);
768 return FAIL;
769}
770
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200771#if defined(FEAT_PERSISTENT_UNDO) || defined(PROTO)
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200772
Bram Moolenaare38eab22019-12-05 21:50:01 +0100773# define UF_START_MAGIC "Vim\237UnDo\345" // magic at start of undofile
Bram Moolenaar9db58062010-05-29 20:33:07 +0200774# define UF_START_MAGIC_LEN 9
Bram Moolenaare38eab22019-12-05 21:50:01 +0100775# define UF_HEADER_MAGIC 0x5fd0 // magic at start of header
776# define UF_HEADER_END_MAGIC 0xe7aa // magic after last header
777# define UF_ENTRY_MAGIC 0xf518 // magic at start of entry
778# define UF_ENTRY_END_MAGIC 0x3581 // magic after last entry
779# define UF_VERSION 2 // 2-byte undofile version number
780# define UF_VERSION_CRYPT 0x8002 // idem, encrypted
Bram Moolenaara800b422010-06-27 01:15:55 +0200781
Bram Moolenaare38eab22019-12-05 21:50:01 +0100782// extra fields for header
Bram Moolenaara800b422010-06-27 01:15:55 +0200783# define UF_LAST_SAVE_NR 1
784
Bram Moolenaare38eab22019-12-05 21:50:01 +0100785// extra fields for uhp
Bram Moolenaara800b422010-06-27 01:15:55 +0200786# define UHP_SAVE_NR 1
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200787
788/*
789 * Compute the hash for the current buffer text into hash[UNDO_HASH_SIZE].
790 */
791 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100792u_compute_hash(char_u *hash)
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200793{
794 context_sha256_T ctx;
795 linenr_T lnum;
796 char_u *p;
797
798 sha256_start(&ctx);
Bram Moolenaarae7ba982011-12-08 15:14:09 +0100799 for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum)
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200800 {
801 p = ml_get(lnum);
Bram Moolenaar442b4222010-05-24 21:34:22 +0200802 sha256_update(&ctx, p, (UINT32_T)(STRLEN(p) + 1));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200803 }
804 sha256_finish(&ctx, hash);
805}
806
807/*
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200808 * Return an allocated string of the full path of the target undofile.
809 * When "reading" is TRUE find the file to read, go over all directories in
810 * 'undodir'.
811 * When "reading" is FALSE use the first name where the directory exists.
Bram Moolenaar9db58062010-05-29 20:33:07 +0200812 * Returns NULL when there is no place to write or no file to read.
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200813 */
Bram Moolenaar840d16f2019-09-10 21:27:18 +0200814 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100815u_get_undo_file_name(char_u *buf_ffname, int reading)
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200816{
817 char_u *dirp;
818 char_u dir_name[IOSIZE + 1];
819 char_u *munged_name = NULL;
820 char_u *undo_file_name = NULL;
821 int dir_len;
822 char_u *p;
Bram Moolenaar8767f522016-07-01 17:17:39 +0200823 stat_T st;
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200824 char_u *ffname = buf_ffname;
825#ifdef HAVE_READLINK
826 char_u fname_buf[MAXPATHL];
827#endif
828
829 if (ffname == NULL)
830 return NULL;
831
832#ifdef HAVE_READLINK
Bram Moolenaare38eab22019-12-05 21:50:01 +0100833 // Expand symlink in the file name, so that we put the undo file with the
834 // actual file instead of with the symlink.
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200835 if (resolve_symlink(ffname, fname_buf) == OK)
836 ffname = fname_buf;
837#endif
838
Bram Moolenaare38eab22019-12-05 21:50:01 +0100839 // Loop over 'undodir'. When reading find the first file that exists.
840 // When not reading use the first directory that exists or ".".
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200841 dirp = p_udir;
842 while (*dirp != NUL)
843 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +0200844 dir_len = copy_option_part(&dirp, dir_name, IOSIZE, ",");
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200845 if (dir_len == 1 && dir_name[0] == '.')
846 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100847 // Use same directory as the ffname,
848 // "dir/name" -> "dir/.name.un~"
Bram Moolenaar71ccd032020-06-12 22:59:11 +0200849 undo_file_name = vim_strnsave(ffname, STRLEN(ffname) + 5);
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200850 if (undo_file_name == NULL)
851 break;
852 p = gettail(undo_file_name);
Bram Moolenaar206f0112014-03-12 16:51:55 +0100853#ifdef VMS
Bram Moolenaare38eab22019-12-05 21:50:01 +0100854 // VMS can not handle more than one dot in the filenames
855 // use "dir/name" -> "dir/_un_name" - add _un_
856 // at the beginning to keep the extension
Bram Moolenaar206f0112014-03-12 16:51:55 +0100857 mch_memmove(p + 4, p, STRLEN(p) + 1);
858 mch_memmove(p, "_un_", 4);
859
860#else
Bram Moolenaare38eab22019-12-05 21:50:01 +0100861 // Use same directory as the ffname,
862 // "dir/name" -> "dir/.name.un~"
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200863 mch_memmove(p + 1, p, STRLEN(p) + 1);
864 *p = '.';
865 STRCAT(p, ".un~");
Bram Moolenaar206f0112014-03-12 16:51:55 +0100866#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200867 }
868 else
869 {
870 dir_name[dir_len] = NUL;
871 if (mch_isdir(dir_name))
872 {
873 if (munged_name == NULL)
874 {
875 munged_name = vim_strsave(ffname);
876 if (munged_name == NULL)
877 return NULL;
Bram Moolenaar91acfff2017-03-12 19:22:36 +0100878 for (p = munged_name; *p != NUL; MB_PTR_ADV(p))
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200879 if (vim_ispathsep(*p))
880 *p = '%';
881 }
882 undo_file_name = concat_fnames(dir_name, munged_name, TRUE);
883 }
884 }
885
Bram Moolenaare38eab22019-12-05 21:50:01 +0100886 // When reading check if the file exists.
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200887 if (undo_file_name != NULL && (!reading
888 || mch_stat((char *)undo_file_name, &st) >= 0))
889 break;
Bram Moolenaard23a8232018-02-10 18:45:26 +0100890 VIM_CLEAR(undo_file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200891 }
892
893 vim_free(munged_name);
894 return undo_file_name;
895}
896
Bram Moolenaar9db58062010-05-29 20:33:07 +0200897 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100898corruption_error(char *mesg, char_u *file_name)
Bram Moolenaar9db58062010-05-29 20:33:07 +0200899{
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +0000900 semsg(_(e_corrupted_undo_file_str_str), mesg, file_name);
Bram Moolenaar9db58062010-05-29 20:33:07 +0200901}
902
903 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100904u_free_uhp(u_header_T *uhp)
Bram Moolenaar9db58062010-05-29 20:33:07 +0200905{
906 u_entry_T *nuep;
907 u_entry_T *uep;
908
909 uep = uhp->uh_entry;
910 while (uep != NULL)
911 {
912 nuep = uep->ue_next;
913 u_freeentry(uep, uep->ue_size);
914 uep = nuep;
915 }
916 vim_free(uhp);
917}
918
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200919/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200920 * Write a sequence of bytes to the undo file.
921 * Buffers and encrypts as needed.
922 * Returns OK or FAIL.
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200923 */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200924 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100925undo_write(bufinfo_T *bi, char_u *ptr, size_t len)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200926{
927#ifdef FEAT_CRYPT
928 if (bi->bi_buffer != NULL)
929 {
930 size_t len_todo = len;
931 char_u *p = ptr;
932
933 while (bi->bi_used + len_todo >= CRYPT_BUF_SIZE)
934 {
935 size_t n = CRYPT_BUF_SIZE - bi->bi_used;
936
937 mch_memmove(bi->bi_buffer + bi->bi_used, p, n);
938 len_todo -= n;
939 p += n;
940 bi->bi_used = CRYPT_BUF_SIZE;
941 if (undo_flush(bi) == FAIL)
942 return FAIL;
943 }
944 if (len_todo > 0)
945 {
946 mch_memmove(bi->bi_buffer + bi->bi_used, p, len_todo);
947 bi->bi_used += len_todo;
948 }
949 return OK;
950 }
951#endif
952 if (fwrite(ptr, len, (size_t)1, bi->bi_fp) != 1)
953 return FAIL;
954 return OK;
955}
956
957#ifdef FEAT_CRYPT
958 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100959undo_flush(bufinfo_T *bi)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200960{
Bram Moolenaar829aa642017-08-23 22:32:35 +0200961 if (bi->bi_buffer != NULL && bi->bi_state != NULL && bi->bi_used > 0)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200962 {
Christian Brabandtf573c6e2021-06-20 14:02:16 +0200963 // Last parameter is only used for sodium encryption and that
964 // explicitly disables encryption of undofiles.
965 crypt_encode_inplace(bi->bi_state, bi->bi_buffer, bi->bi_used, FALSE);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200966 if (fwrite(bi->bi_buffer, bi->bi_used, (size_t)1, bi->bi_fp) != 1)
967 return FAIL;
968 bi->bi_used = 0;
969 }
970 return OK;
971}
972#endif
973
974/*
975 * Write "ptr[len]" and crypt the bytes when needed.
976 * Returns OK or FAIL.
977 */
978 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100979fwrite_crypt(bufinfo_T *bi, char_u *ptr, size_t len)
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200980{
981#ifdef FEAT_CRYPT
982 char_u *copy;
983 char_u small_buf[100];
984 size_t i;
985
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200986 if (bi->bi_state != NULL && bi->bi_buffer == NULL)
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200987 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100988 // crypting every piece of text separately
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200989 if (len < 100)
Bram Moolenaare38eab22019-12-05 21:50:01 +0100990 copy = small_buf; // no malloc()/free() for short strings
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200991 else
992 {
993 copy = lalloc(len, FALSE);
994 if (copy == NULL)
995 return 0;
996 }
Christian Brabandtf573c6e2021-06-20 14:02:16 +0200997 // Last parameter is only used for sodium encryption and that
998 // explicitly disables encryption of undofiles.
999 crypt_encode(bi->bi_state, ptr, len, copy, TRUE);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001000 i = fwrite(copy, len, (size_t)1, bi->bi_fp);
1001 if (copy != small_buf)
1002 vim_free(copy);
1003 return i == 1 ? OK : FAIL;
Bram Moolenaar191e0a22010-06-14 01:39:13 +02001004 }
Bram Moolenaar191e0a22010-06-14 01:39:13 +02001005#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001006 return undo_write(bi, ptr, len);
Bram Moolenaar191e0a22010-06-14 01:39:13 +02001007}
1008
1009/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001010 * Write a number, MSB first, in "len" bytes.
1011 * Must match with undo_read_?c() functions.
1012 * Returns OK or FAIL.
Bram Moolenaar191e0a22010-06-14 01:39:13 +02001013 */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001014 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001015undo_write_bytes(bufinfo_T *bi, long_u nr, int len)
Bram Moolenaar191e0a22010-06-14 01:39:13 +02001016{
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001017 char_u buf[8];
1018 int i;
1019 int bufi = 0;
Bram Moolenaar191e0a22010-06-14 01:39:13 +02001020
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001021 for (i = len - 1; i >= 0; --i)
Bram Moolenaar9b8f0212014-08-13 22:05:53 +02001022 buf[bufi++] = (char_u)(nr >> (i * 8));
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001023 return undo_write(bi, buf, (size_t)len);
1024}
1025
1026/*
1027 * Write the pointer to an undo header. Instead of writing the pointer itself
1028 * we use the sequence number of the header. This is converted back to
1029 * pointers when reading. */
1030 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001031put_header_ptr(bufinfo_T *bi, u_header_T *uhp)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001032{
1033 undo_write_bytes(bi, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
Bram Moolenaar191e0a22010-06-14 01:39:13 +02001034}
1035
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001036 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001037undo_read_4c(bufinfo_T *bi)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001038{
1039#ifdef FEAT_CRYPT
1040 if (bi->bi_buffer != NULL)
1041 {
1042 char_u buf[4];
1043 int n;
1044
1045 undo_read(bi, buf, (size_t)4);
Bram Moolenaar35169282014-09-11 22:50:09 +02001046 n = ((unsigned)buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3];
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001047 return n;
1048 }
1049#endif
1050 return get4c(bi->bi_fp);
1051}
1052
1053 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001054undo_read_2c(bufinfo_T *bi)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001055{
1056#ifdef FEAT_CRYPT
1057 if (bi->bi_buffer != NULL)
1058 {
1059 char_u buf[2];
1060 int n;
1061
1062 undo_read(bi, buf, (size_t)2);
1063 n = (buf[0] << 8) + buf[1];
1064 return n;
1065 }
1066#endif
1067 return get2c(bi->bi_fp);
1068}
1069
1070 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001071undo_read_byte(bufinfo_T *bi)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001072{
1073#ifdef FEAT_CRYPT
1074 if (bi->bi_buffer != NULL)
1075 {
1076 char_u buf[1];
1077
1078 undo_read(bi, buf, (size_t)1);
1079 return buf[0];
1080 }
1081#endif
1082 return getc(bi->bi_fp);
1083}
1084
1085 static time_t
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001086undo_read_time(bufinfo_T *bi)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001087{
1088#ifdef FEAT_CRYPT
1089 if (bi->bi_buffer != NULL)
1090 {
1091 char_u buf[8];
1092 time_t n = 0;
1093 int i;
1094
1095 undo_read(bi, buf, (size_t)8);
1096 for (i = 0; i < 8; ++i)
1097 n = (n << 8) + buf[i];
1098 return n;
1099 }
1100#endif
1101 return get8ctime(bi->bi_fp);
1102}
1103
1104/*
1105 * Read "buffer[size]" from the undo file.
1106 * Return OK or FAIL.
1107 */
1108 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001109undo_read(bufinfo_T *bi, char_u *buffer, size_t size)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001110{
Bram Moolenaar56f2db52017-06-11 23:09:15 +02001111 int retval = OK;
1112
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001113#ifdef FEAT_CRYPT
1114 if (bi->bi_buffer != NULL)
1115 {
Bram Moolenaar9b8f0212014-08-13 22:05:53 +02001116 int size_todo = (int)size;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001117 char_u *p = buffer;
1118
1119 while (size_todo > 0)
1120 {
1121 size_t n;
1122
1123 if (bi->bi_used >= bi->bi_avail)
1124 {
1125 n = fread(bi->bi_buffer, 1, (size_t)CRYPT_BUF_SIZE, bi->bi_fp);
Bram Moolenaar55952d42016-11-05 14:58:34 +01001126 if (n == 0)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001127 {
Bram Moolenaar56f2db52017-06-11 23:09:15 +02001128 retval = FAIL;
1129 break;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001130 }
1131 bi->bi_avail = n;
1132 bi->bi_used = 0;
Christian Brabandtf573c6e2021-06-20 14:02:16 +02001133 crypt_decode_inplace(bi->bi_state, bi->bi_buffer, bi->bi_avail, FALSE);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001134 }
1135 n = size_todo;
1136 if (n > bi->bi_avail - bi->bi_used)
1137 n = bi->bi_avail - bi->bi_used;
1138 mch_memmove(p, bi->bi_buffer + bi->bi_used, n);
1139 bi->bi_used += n;
Bram Moolenaar9b8f0212014-08-13 22:05:53 +02001140 size_todo -= (int)n;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001141 p += n;
1142 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001143 }
Bram Moolenaar56f2db52017-06-11 23:09:15 +02001144 else
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001145#endif
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00001146 if (fread(buffer, size, 1, bi->bi_fp) != 1)
Bram Moolenaar56f2db52017-06-11 23:09:15 +02001147 retval = FAIL;
1148
1149 if (retval == FAIL)
Bram Moolenaare38eab22019-12-05 21:50:01 +01001150 // Error may be checked for only later. Fill with zeros,
1151 // so that the reader won't use garbage.
Bram Moolenaar56f2db52017-06-11 23:09:15 +02001152 vim_memset(buffer, 0, size);
1153 return retval;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001154}
1155
1156/*
1157 * Read a string of length "len" from "bi->bi_fd".
1158 * "len" can be zero to allocate an empty line.
1159 * Decrypt the bytes if needed.
1160 * Append a NUL.
1161 * Returns a pointer to allocated memory or NULL for failure.
1162 */
1163 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001164read_string_decrypt(bufinfo_T *bi, int len)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001165{
Bram Moolenaar964b3742019-05-24 18:54:09 +02001166 char_u *ptr = alloc(len + 1);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001167
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00001168 if (ptr == NULL)
1169 return NULL;
1170
1171 if (len > 0 && undo_read(bi, ptr, len) == FAIL)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001172 {
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00001173 vim_free(ptr);
1174 return NULL;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001175 }
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00001176 // 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.
1178 ptr[len] = NUL;
1179#ifdef FEAT_CRYPT
1180 if (bi->bi_state != NULL && bi->bi_buffer == NULL)
1181 crypt_decode_inplace(bi->bi_state, ptr, len, FALSE);
1182#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001183 return ptr;
1184}
1185
1186/*
1187 * Writes the (not encrypted) header and initializes encryption if needed.
1188 */
1189 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001190serialize_header(bufinfo_T *bi, char_u *hash)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001191{
Bram Moolenaarccae4672019-01-04 15:09:57 +01001192 long len;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001193 buf_T *buf = bi->bi_buf;
1194 FILE *fp = bi->bi_fp;
1195 char_u time_buf[8];
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001196
Bram Moolenaare38eab22019-12-05 21:50:01 +01001197 // Start writing, first the magic marker and undo info version.
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001198 if (fwrite(UF_START_MAGIC, (size_t)UF_START_MAGIC_LEN, (size_t)1, fp) != 1)
1199 return FAIL;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001200
Bram Moolenaare38eab22019-12-05 21:50:01 +01001201 // If the buffer is encrypted then all text bytes following will be
1202 // encrypted. Numbers and other info is not crypted.
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001203#ifdef FEAT_CRYPT
Bram Moolenaarfa0ff9a2010-07-25 16:05:19 +02001204 if (*buf->b_p_key != NUL)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001205 {
1206 char_u *header;
1207 int header_len;
1208
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001209 undo_write_bytes(bi, (long_u)UF_VERSION_CRYPT, 2);
1210 bi->bi_state = crypt_create_for_writing(crypt_get_method_nr(buf),
1211 buf->b_p_key, &header, &header_len);
1212 if (bi->bi_state == NULL)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001213 return FAIL;
Bram Moolenaarccae4672019-01-04 15:09:57 +01001214 len = (long)fwrite(header, (size_t)header_len, (size_t)1, fp);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001215 vim_free(header);
1216 if (len != 1)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001217 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001218 crypt_free_state(bi->bi_state);
1219 bi->bi_state = NULL;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001220 return FAIL;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001221 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001222
1223 if (crypt_whole_undofile(crypt_get_method_nr(buf)))
1224 {
1225 bi->bi_buffer = alloc(CRYPT_BUF_SIZE);
1226 if (bi->bi_buffer == NULL)
1227 {
1228 crypt_free_state(bi->bi_state);
1229 bi->bi_state = NULL;
1230 return FAIL;
1231 }
1232 bi->bi_used = 0;
1233 }
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001234 }
1235 else
1236#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001237 undo_write_bytes(bi, (long_u)UF_VERSION, 2);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001238
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001239
Bram Moolenaare38eab22019-12-05 21:50:01 +01001240 // Write a hash of the buffer text, so that we can verify it is still the
1241 // same when reading the buffer text.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001242 if (undo_write(bi, hash, (size_t)UNDO_HASH_SIZE) == FAIL)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001243 return FAIL;
1244
Bram Moolenaare38eab22019-12-05 21:50:01 +01001245 // buffer-specific data
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001246 undo_write_bytes(bi, (long_u)buf->b_ml.ml_line_count, 4);
Bram Moolenaarccae4672019-01-04 15:09:57 +01001247 len = buf->b_u_line_ptr.ul_line == NULL
Bram Moolenaar8aef43b2019-01-08 20:14:35 +01001248 ? 0L : (long)STRLEN(buf->b_u_line_ptr.ul_line);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001249 undo_write_bytes(bi, (long_u)len, 4);
Bram Moolenaar8aef43b2019-01-08 20:14:35 +01001250 if (len > 0 && fwrite_crypt(bi, buf->b_u_line_ptr.ul_line, (size_t)len)
1251 == FAIL)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001252 return FAIL;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001253 undo_write_bytes(bi, (long_u)buf->b_u_line_lnum, 4);
1254 undo_write_bytes(bi, (long_u)buf->b_u_line_colnr, 4);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001255
Bram Moolenaare38eab22019-12-05 21:50:01 +01001256 // Undo structures header data
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001257 put_header_ptr(bi, buf->b_u_oldhead);
1258 put_header_ptr(bi, buf->b_u_newhead);
1259 put_header_ptr(bi, buf->b_u_curhead);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001260
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001261 undo_write_bytes(bi, (long_u)buf->b_u_numhead, 4);
1262 undo_write_bytes(bi, (long_u)buf->b_u_seq_last, 4);
1263 undo_write_bytes(bi, (long_u)buf->b_u_seq_cur, 4);
1264 time_to_bytes(buf->b_u_time_cur, time_buf);
1265 undo_write(bi, time_buf, 8);
Bram Moolenaara800b422010-06-27 01:15:55 +02001266
Bram Moolenaare38eab22019-12-05 21:50:01 +01001267 // Optional fields.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001268 undo_write_bytes(bi, 4, 1);
1269 undo_write_bytes(bi, UF_LAST_SAVE_NR, 1);
1270 undo_write_bytes(bi, (long_u)buf->b_u_save_nr_last, 4);
Bram Moolenaara800b422010-06-27 01:15:55 +02001271
Bram Moolenaare38eab22019-12-05 21:50:01 +01001272 undo_write_bytes(bi, 0, 1); // end marker
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001273
1274 return OK;
1275}
1276
1277 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001278serialize_uhp(bufinfo_T *bi, u_header_T *uhp)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001279{
1280 int i;
1281 u_entry_T *uep;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001282 char_u time_buf[8];
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001283
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001284 if (undo_write_bytes(bi, (long_u)UF_HEADER_MAGIC, 2) == FAIL)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001285 return FAIL;
1286
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001287 put_header_ptr(bi, uhp->uh_next.ptr);
1288 put_header_ptr(bi, uhp->uh_prev.ptr);
1289 put_header_ptr(bi, uhp->uh_alt_next.ptr);
1290 put_header_ptr(bi, uhp->uh_alt_prev.ptr);
1291 undo_write_bytes(bi, uhp->uh_seq, 4);
1292 serialize_pos(bi, uhp->uh_cursor);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001293 undo_write_bytes(bi, (long_u)uhp->uh_cursor_vcol, 4);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001294 undo_write_bytes(bi, (long_u)uhp->uh_flags, 2);
Bram Moolenaare38eab22019-12-05 21:50:01 +01001295 // Assume NMARKS will stay the same.
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001296 for (i = 0; i < NMARKS; ++i)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001297 serialize_pos(bi, uhp->uh_namedm[i]);
1298 serialize_visualinfo(bi, &uhp->uh_visual);
1299 time_to_bytes(uhp->uh_time, time_buf);
1300 undo_write(bi, time_buf, 8);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001301
Bram Moolenaare38eab22019-12-05 21:50:01 +01001302 // Optional fields.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001303 undo_write_bytes(bi, 4, 1);
1304 undo_write_bytes(bi, UHP_SAVE_NR, 1);
1305 undo_write_bytes(bi, (long_u)uhp->uh_save_nr, 4);
Bram Moolenaara800b422010-06-27 01:15:55 +02001306
Bram Moolenaare38eab22019-12-05 21:50:01 +01001307 undo_write_bytes(bi, 0, 1); // end marker
Bram Moolenaara800b422010-06-27 01:15:55 +02001308
Bram Moolenaare38eab22019-12-05 21:50:01 +01001309 // Write all the entries.
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001310 for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
1311 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001312 undo_write_bytes(bi, (long_u)UF_ENTRY_MAGIC, 2);
1313 if (serialize_uep(bi, uep) == FAIL)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001314 return FAIL;
1315 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001316 undo_write_bytes(bi, (long_u)UF_ENTRY_END_MAGIC, 2);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001317 return OK;
1318}
1319
1320 static u_header_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001321unserialize_uhp(bufinfo_T *bi, char_u *file_name)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001322{
1323 u_header_T *uhp;
1324 int i;
1325 u_entry_T *uep, *last_uep;
1326 int c;
1327 int error;
1328
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001329 uhp = U_ALLOC_LINE(sizeof(u_header_T));
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001330 if (uhp == NULL)
1331 return NULL;
Bram Moolenaara80faa82020-04-12 19:37:17 +02001332 CLEAR_POINTER(uhp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001333#ifdef U_DEBUG
1334 uhp->uh_magic = UH_MAGIC;
1335#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001336 uhp->uh_next.seq = undo_read_4c(bi);
1337 uhp->uh_prev.seq = undo_read_4c(bi);
1338 uhp->uh_alt_next.seq = undo_read_4c(bi);
1339 uhp->uh_alt_prev.seq = undo_read_4c(bi);
1340 uhp->uh_seq = undo_read_4c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001341 if (uhp->uh_seq <= 0)
1342 {
1343 corruption_error("uh_seq", file_name);
1344 vim_free(uhp);
1345 return NULL;
1346 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001347 unserialize_pos(bi, &uhp->uh_cursor);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001348 uhp->uh_cursor_vcol = undo_read_4c(bi);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001349 uhp->uh_flags = undo_read_2c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001350 for (i = 0; i < NMARKS; ++i)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001351 unserialize_pos(bi, &uhp->uh_namedm[i]);
1352 unserialize_visualinfo(bi, &uhp->uh_visual);
1353 uhp->uh_time = undo_read_time(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001354
Bram Moolenaare38eab22019-12-05 21:50:01 +01001355 // Optional fields.
Bram Moolenaar69154f22010-07-18 21:42:34 +02001356 for (;;)
1357 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001358 int len = undo_read_byte(bi);
Bram Moolenaar69154f22010-07-18 21:42:34 +02001359 int what;
Bram Moolenaara800b422010-06-27 01:15:55 +02001360
Bram Moolenaarfb06d762019-08-04 18:55:35 +02001361 if (len == EOF)
1362 {
1363 corruption_error("truncated", file_name);
1364 u_free_uhp(uhp);
1365 return NULL;
1366 }
Bram Moolenaar69154f22010-07-18 21:42:34 +02001367 if (len == 0)
1368 break;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001369 what = undo_read_byte(bi);
Bram Moolenaar69154f22010-07-18 21:42:34 +02001370 switch (what)
1371 {
1372 case UHP_SAVE_NR:
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001373 uhp->uh_save_nr = undo_read_4c(bi);
Bram Moolenaara800b422010-06-27 01:15:55 +02001374 break;
Bram Moolenaar69154f22010-07-18 21:42:34 +02001375 default:
Bram Moolenaare38eab22019-12-05 21:50:01 +01001376 // field not supported, skip
Bram Moolenaar69154f22010-07-18 21:42:34 +02001377 while (--len >= 0)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001378 (void)undo_read_byte(bi);
Bram Moolenaara800b422010-06-27 01:15:55 +02001379 }
Bram Moolenaar69154f22010-07-18 21:42:34 +02001380 }
Bram Moolenaara800b422010-06-27 01:15:55 +02001381
Bram Moolenaare38eab22019-12-05 21:50:01 +01001382 // Unserialize the uep list.
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001383 last_uep = NULL;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001384 while ((c = undo_read_2c(bi)) == UF_ENTRY_MAGIC)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001385 {
1386 error = FALSE;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001387 uep = unserialize_uep(bi, &error, file_name);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001388 if (last_uep == NULL)
1389 uhp->uh_entry = uep;
1390 else
1391 last_uep->ue_next = uep;
1392 last_uep = uep;
1393 if (uep == NULL || error)
1394 {
1395 u_free_uhp(uhp);
1396 return NULL;
1397 }
1398 }
1399 if (c != UF_ENTRY_END_MAGIC)
1400 {
1401 corruption_error("entry end", file_name);
1402 u_free_uhp(uhp);
1403 return NULL;
1404 }
1405
1406 return uhp;
1407}
1408
Bram Moolenaar9db58062010-05-29 20:33:07 +02001409/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001410 * Serialize "uep".
Bram Moolenaar9db58062010-05-29 20:33:07 +02001411 */
1412 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001413serialize_uep(
1414 bufinfo_T *bi,
1415 u_entry_T *uep)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001416{
1417 int i;
1418 size_t len;
1419
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001420 undo_write_bytes(bi, (long_u)uep->ue_top, 4);
1421 undo_write_bytes(bi, (long_u)uep->ue_bot, 4);
1422 undo_write_bytes(bi, (long_u)uep->ue_lcount, 4);
1423 undo_write_bytes(bi, (long_u)uep->ue_size, 4);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001424 for (i = 0; i < uep->ue_size; ++i)
1425 {
Bram Moolenaarccae4672019-01-04 15:09:57 +01001426 // Text is written without the text properties, since we cannot restore
1427 // the text property types.
1428 len = STRLEN(uep->ue_array[i].ul_line);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001429 if (undo_write_bytes(bi, (long_u)len, 4) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001430 return FAIL;
Bram Moolenaarccae4672019-01-04 15:09:57 +01001431 if (len > 0 && fwrite_crypt(bi, uep->ue_array[i].ul_line, len) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001432 return FAIL;
1433 }
1434 return OK;
1435}
1436
1437 static u_entry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001438unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001439{
1440 int i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001441 u_entry_T *uep;
Bram Moolenaarccae4672019-01-04 15:09:57 +01001442 undoline_T *array = NULL;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001443 char_u *line;
1444 int line_len;
1445
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001446 uep = U_ALLOC_LINE(sizeof(u_entry_T));
Bram Moolenaar9db58062010-05-29 20:33:07 +02001447 if (uep == NULL)
1448 return NULL;
Bram Moolenaara80faa82020-04-12 19:37:17 +02001449 CLEAR_POINTER(uep);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001450#ifdef U_DEBUG
1451 uep->ue_magic = UE_MAGIC;
1452#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001453 uep->ue_top = undo_read_4c(bi);
1454 uep->ue_bot = undo_read_4c(bi);
1455 uep->ue_lcount = undo_read_4c(bi);
1456 uep->ue_size = undo_read_4c(bi);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001457 if (uep->ue_size > 0)
1458 {
Bram Moolenaar0c8485f2017-02-26 18:17:10 +01001459 if (uep->ue_size < LONG_MAX / (int)sizeof(char_u *))
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001460 array = U_ALLOC_LINE(sizeof(undoline_T) * uep->ue_size);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001461 if (array == NULL)
1462 {
1463 *error = TRUE;
1464 return uep;
1465 }
Bram Moolenaarccae4672019-01-04 15:09:57 +01001466 vim_memset(array, 0, sizeof(undoline_T) * uep->ue_size);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001467 }
1468 uep->ue_array = array;
1469
1470 for (i = 0; i < uep->ue_size; ++i)
1471 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001472 line_len = undo_read_4c(bi);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001473 if (line_len >= 0)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001474 line = read_string_decrypt(bi, line_len);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001475 else
1476 {
1477 line = NULL;
1478 corruption_error("line length", file_name);
1479 }
1480 if (line == NULL)
1481 {
1482 *error = TRUE;
1483 return uep;
1484 }
Bram Moolenaarccae4672019-01-04 15:09:57 +01001485 array[i].ul_line = line;
1486 array[i].ul_len = line_len + 1;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001487 }
1488 return uep;
1489}
1490
1491/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001492 * Serialize "pos".
Bram Moolenaar9db58062010-05-29 20:33:07 +02001493 */
1494 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001495serialize_pos(bufinfo_T *bi, pos_T pos)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001496{
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001497 undo_write_bytes(bi, (long_u)pos.lnum, 4);
1498 undo_write_bytes(bi, (long_u)pos.col, 4);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001499 undo_write_bytes(bi, (long_u)pos.coladd, 4);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001500}
1501
1502/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001503 * Unserialize the pos_T at the current position.
Bram Moolenaar9db58062010-05-29 20:33:07 +02001504 */
1505 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001506unserialize_pos(bufinfo_T *bi, pos_T *pos)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001507{
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001508 pos->lnum = undo_read_4c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001509 if (pos->lnum < 0)
1510 pos->lnum = 0;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001511 pos->col = undo_read_4c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001512 if (pos->col < 0)
1513 pos->col = 0;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001514 pos->coladd = undo_read_4c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001515 if (pos->coladd < 0)
1516 pos->coladd = 0;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001517}
1518
1519/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001520 * Serialize "info".
Bram Moolenaar9db58062010-05-29 20:33:07 +02001521 */
1522 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001523serialize_visualinfo(bufinfo_T *bi, visualinfo_T *info)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001524{
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001525 serialize_pos(bi, info->vi_start);
1526 serialize_pos(bi, info->vi_end);
1527 undo_write_bytes(bi, (long_u)info->vi_mode, 4);
1528 undo_write_bytes(bi, (long_u)info->vi_curswant, 4);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001529}
1530
1531/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001532 * Unserialize the visualinfo_T at the current position.
Bram Moolenaar9db58062010-05-29 20:33:07 +02001533 */
1534 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001535unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001536{
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001537 unserialize_pos(bi, &info->vi_start);
1538 unserialize_pos(bi, &info->vi_end);
1539 info->vi_mode = undo_read_4c(bi);
1540 info->vi_curswant = undo_read_4c(bi);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001541}
1542
1543/*
1544 * Write the undo tree in an undo file.
1545 * When "name" is not NULL, use it as the name of the undo file.
1546 * Otherwise use buf->b_ffname to generate the undo file name.
1547 * "buf" must never be null, buf->b_ffname is used to obtain the original file
1548 * permissions.
1549 * "forceit" is TRUE for ":wundo!", FALSE otherwise.
1550 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
1551 */
1552 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001553u_write_undo(
1554 char_u *name,
1555 int forceit,
1556 buf_T *buf,
1557 char_u *hash)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001558{
1559 u_header_T *uhp;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001560 char_u *file_name;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001561 int mark;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001562#ifdef U_DEBUG
1563 int headers_written = 0;
1564#endif
1565 int fd;
1566 FILE *fp = NULL;
1567 int perm;
1568 int write_ok = FALSE;
1569#ifdef UNIX
1570 int st_old_valid = FALSE;
Bram Moolenaar8767f522016-07-01 17:17:39 +02001571 stat_T st_old;
1572 stat_T st_new;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001573#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001574 bufinfo_T bi;
1575
Bram Moolenaara80faa82020-04-12 19:37:17 +02001576 CLEAR_FIELD(bi);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001577
1578 if (name == NULL)
1579 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001580 file_name = u_get_undo_file_name(buf->b_ffname, FALSE);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001581 if (file_name == NULL)
1582 {
1583 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001584 {
1585 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001586 smsg(
Bram Moolenaar504a8212010-05-30 17:17:42 +02001587 _("Cannot write undo file in any directory in 'undodir'"));
1588 verbose_leave();
1589 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001590 return;
1591 }
1592 }
1593 else
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001594 file_name = name;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001595
1596 /*
1597 * Decide about the permission to use for the undo file. If the buffer
1598 * has a name use the permission of the original file. Otherwise only
1599 * allow the user to access the undo file.
1600 */
1601 perm = 0600;
1602 if (buf->b_ffname != NULL)
1603 {
1604#ifdef UNIX
1605 if (mch_stat((char *)buf->b_ffname, &st_old) >= 0)
1606 {
1607 perm = st_old.st_mode;
1608 st_old_valid = TRUE;
1609 }
1610#else
1611 perm = mch_getperm(buf->b_ffname);
1612 if (perm < 0)
1613 perm = 0600;
1614#endif
1615 }
1616
Bram Moolenaare38eab22019-12-05 21:50:01 +01001617 // strip any s-bit and executable bit
Bram Moolenaar3cbac302015-04-21 16:12:06 +02001618 perm = perm & 0666;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001619
Bram Moolenaare38eab22019-12-05 21:50:01 +01001620 // If the undo file already exists, verify that it actually is an undo
1621 // file, and delete it.
Bram Moolenaar9db58062010-05-29 20:33:07 +02001622 if (mch_getperm(file_name) >= 0)
1623 {
1624 if (name == NULL || !forceit)
1625 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01001626 // Check we can read it and it's an undo file.
Bram Moolenaar9db58062010-05-29 20:33:07 +02001627 fd = mch_open((char *)file_name, O_RDONLY|O_EXTRA, 0);
1628 if (fd < 0)
1629 {
1630 if (name != NULL || p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001631 {
1632 if (name == NULL)
1633 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001634 smsg(
Bram Moolenaar504a8212010-05-30 17:17:42 +02001635 _("Will not overwrite with undo file, cannot read: %s"),
Bram Moolenaar9db58062010-05-29 20:33:07 +02001636 file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001637 if (name == NULL)
1638 verbose_leave();
1639 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001640 goto theend;
1641 }
1642 else
1643 {
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001644 char_u mbuf[UF_START_MAGIC_LEN];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001645 int len;
1646
Bram Moolenaar540fc6f2010-12-17 16:27:16 +01001647 len = read_eintr(fd, mbuf, UF_START_MAGIC_LEN);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001648 close(fd);
1649 if (len < UF_START_MAGIC_LEN
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001650 || memcmp(mbuf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001651 {
1652 if (name != NULL || p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001653 {
1654 if (name == NULL)
1655 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001656 smsg(
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001657 _("Will not overwrite, this is not an undo file: %s"),
Bram Moolenaar9db58062010-05-29 20:33:07 +02001658 file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001659 if (name == NULL)
1660 verbose_leave();
1661 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001662 goto theend;
1663 }
1664 }
1665 }
1666 mch_remove(file_name);
1667 }
1668
Bram Moolenaare38eab22019-12-05 21:50:01 +01001669 // If there is no undo information at all, quit here after deleting any
1670 // existing undo file.
Bram Moolenaarccae4672019-01-04 15:09:57 +01001671 if (buf->b_u_numhead == 0 && buf->b_u_line_ptr.ul_line == NULL)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001672 {
1673 if (p_verbose > 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001674 verb_msg(_("Skipping undo file write, nothing to undo"));
Bram Moolenaar504a8212010-05-30 17:17:42 +02001675 goto theend;
1676 }
1677
Bram Moolenaar9db58062010-05-29 20:33:07 +02001678 fd = mch_open((char *)file_name,
1679 O_CREAT|O_EXTRA|O_WRONLY|O_EXCL|O_NOFOLLOW, perm);
1680 if (fd < 0)
1681 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00001682 semsg(_(e_cannot_open_undo_file_for_writing_str), file_name);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001683 goto theend;
1684 }
1685 (void)mch_setperm(file_name, perm);
1686 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001687 {
1688 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001689 smsg(_("Writing undo file: %s"), file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001690 verbose_leave();
1691 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001692
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001693#ifdef U_DEBUG
Bram Moolenaare38eab22019-12-05 21:50:01 +01001694 // Check there is no problem in undo info before writing.
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001695 u_check(FALSE);
1696#endif
1697
Bram Moolenaar9db58062010-05-29 20:33:07 +02001698#ifdef UNIX
1699 /*
1700 * Try to set the group of the undo file same as the original file. If
1701 * this fails, set the protection bits for the group same as the
1702 * protection bits for others.
1703 */
Bram Moolenaarce69e822010-07-21 20:31:07 +02001704 if (st_old_valid
1705 && mch_stat((char *)file_name, &st_new) >= 0
1706 && st_new.st_gid != st_old.st_gid
Bram Moolenaare38eab22019-12-05 21:50:01 +01001707# ifdef HAVE_FCHOWN // sequent-ptx lacks fchown()
Bram Moolenaarce69e822010-07-21 20:31:07 +02001708 && fchown(fd, (uid_t)-1, st_old.st_gid) != 0
Bram Moolenaar9db58062010-05-29 20:33:07 +02001709# endif
1710 )
1711 mch_setperm(file_name, (perm & 0707) | ((perm & 07) << 3));
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02001712# if defined(HAVE_SELINUX) || defined(HAVE_SMACK)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001713 if (buf->b_ffname != NULL)
1714 mch_copy_sec(buf->b_ffname, file_name);
1715# endif
1716#endif
1717
1718 fp = fdopen(fd, "w");
1719 if (fp == NULL)
1720 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00001721 semsg(_(e_cannot_open_undo_file_for_writing_str), file_name);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001722 close(fd);
1723 mch_remove(file_name);
1724 goto theend;
1725 }
1726
Bram Moolenaare38eab22019-12-05 21:50:01 +01001727 // Undo must be synced.
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001728 u_sync(TRUE);
1729
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001730 /*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001731 * Write the header. Initializes encryption, if enabled.
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001732 */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001733 bi.bi_buf = buf;
1734 bi.bi_fp = fp;
1735 if (serialize_header(&bi, hash) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001736 goto write_error;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001737
1738 /*
1739 * Iteratively serialize UHPs and their UEPs from the top down.
1740 */
1741 mark = ++lastmark;
1742 uhp = buf->b_u_oldhead;
1743 while (uhp != NULL)
1744 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01001745 // Serialize current UHP if we haven't seen it
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001746 if (uhp->uh_walk != mark)
1747 {
1748 uhp->uh_walk = mark;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001749#ifdef U_DEBUG
1750 ++headers_written;
1751#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001752 if (serialize_uhp(&bi, uhp) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001753 goto write_error;
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001754 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001755
Bram Moolenaare38eab22019-12-05 21:50:01 +01001756 // Now walk through the tree - algorithm from undo_time().
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001757 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != mark)
1758 uhp = uhp->uh_prev.ptr;
1759 else if (uhp->uh_alt_next.ptr != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001760 && uhp->uh_alt_next.ptr->uh_walk != mark)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001761 uhp = uhp->uh_alt_next.ptr;
1762 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001763 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001764 uhp = uhp->uh_next.ptr;
1765 else if (uhp->uh_alt_prev.ptr != NULL)
1766 uhp = uhp->uh_alt_prev.ptr;
1767 else
1768 uhp = uhp->uh_next.ptr;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001769 }
1770
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001771 if (undo_write_bytes(&bi, (long_u)UF_HEADER_END_MAGIC, 2) == OK)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001772 write_ok = TRUE;
1773#ifdef U_DEBUG
1774 if (headers_written != buf->b_u_numhead)
Bram Moolenaar570064c2013-06-10 20:25:10 +02001775 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001776 semsg("Written %ld headers, ...", headers_written);
1777 semsg("... but numhead is %ld", buf->b_u_numhead);
Bram Moolenaar570064c2013-06-10 20:25:10 +02001778 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001779#endif
1780
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001781#ifdef FEAT_CRYPT
1782 if (bi.bi_state != NULL && undo_flush(&bi) == FAIL)
1783 write_ok = FALSE;
1784#endif
1785
Bram Moolenaar340dd0f2021-10-14 17:52:23 +01001786#if defined(UNIX) && defined(HAVE_FSYNC)
1787 if (p_fs && fflush(fp) == 0 && vim_fsync(fd) != 0)
1788 write_ok = FALSE;
1789#endif
1790
Bram Moolenaar9db58062010-05-29 20:33:07 +02001791write_error:
1792 fclose(fp);
1793 if (!write_ok)
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00001794 semsg(_(e_write_error_in_undo_file_str), file_name);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001795
Bram Moolenaar4f974752019-02-17 17:44:42 +01001796#if defined(MSWIN)
Bram Moolenaare38eab22019-12-05 21:50:01 +01001797 // Copy file attributes; for systems where this can only be done after
1798 // closing the file.
Bram Moolenaar9db58062010-05-29 20:33:07 +02001799 if (buf->b_ffname != NULL)
1800 (void)mch_copy_file_attribute(buf->b_ffname, file_name);
1801#endif
1802#ifdef HAVE_ACL
1803 if (buf->b_ffname != NULL)
1804 {
1805 vim_acl_T acl;
1806
Bram Moolenaare38eab22019-12-05 21:50:01 +01001807 // For systems that support ACL: get the ACL from the original file.
Bram Moolenaar9db58062010-05-29 20:33:07 +02001808 acl = mch_get_acl(buf->b_ffname);
1809 mch_set_acl(file_name, acl);
Bram Moolenaard2aed442012-06-01 13:46:12 +02001810 mch_free_acl(acl);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001811 }
1812#endif
1813
1814theend:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001815#ifdef FEAT_CRYPT
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001816 if (bi.bi_state != NULL)
1817 crypt_free_state(bi.bi_state);
1818 vim_free(bi.bi_buffer);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001819#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02001820 if (file_name != name)
1821 vim_free(file_name);
1822}
1823
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001824/*
1825 * Load the undo tree from an undo file.
1826 * If "name" is not NULL use it as the undo file name. This also means being
1827 * a bit more verbose.
1828 * Otherwise use curbuf->b_ffname to generate the undo file name.
1829 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
1830 */
1831 void
Bram Moolenaarbd67aac2019-09-21 23:09:04 +02001832u_read_undo(char_u *name, char_u *hash, char_u *orig_name UNUSED)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001833{
1834 char_u *file_name;
1835 FILE *fp;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001836 long version, str_len;
Bram Moolenaarccae4672019-01-04 15:09:57 +01001837 undoline_T line_ptr;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001838 linenr_T line_lnum;
1839 colnr_T line_colnr;
1840 linenr_T line_count;
Bram Moolenaar3eb16372017-02-26 18:11:36 +01001841 long num_head = 0;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001842 long old_header_seq, new_header_seq, cur_header_seq;
1843 long seq_last, seq_cur;
Bram Moolenaarb2c03502010-07-02 20:20:09 +02001844 long last_save_nr = 0;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001845 short old_idx = -1, new_idx = -1, cur_idx = -1;
1846 long num_read_uhps = 0;
1847 time_t seq_time;
1848 int i, j;
1849 int c;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001850 u_header_T *uhp;
1851 u_header_T **uhp_table = NULL;
1852 char_u read_hash[UNDO_HASH_SIZE];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001853 char_u magic_buf[UF_START_MAGIC_LEN];
1854#ifdef U_DEBUG
1855 int *uhp_table_used;
1856#endif
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001857#ifdef UNIX
Bram Moolenaar8767f522016-07-01 17:17:39 +02001858 stat_T st_orig;
1859 stat_T st_undo;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001860#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001861 bufinfo_T bi;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001862
Bram Moolenaara80faa82020-04-12 19:37:17 +02001863 CLEAR_FIELD(bi);
Bram Moolenaarccae4672019-01-04 15:09:57 +01001864 line_ptr.ul_len = 0;
1865 line_ptr.ul_line = NULL;
1866
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001867 if (name == NULL)
1868 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001869 file_name = u_get_undo_file_name(curbuf->b_ffname, TRUE);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001870 if (file_name == NULL)
1871 return;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001872
1873#ifdef UNIX
Bram Moolenaare38eab22019-12-05 21:50:01 +01001874 // For safety we only read an undo file if the owner is equal to the
1875 // owner of the text file or equal to the current user.
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001876 if (mch_stat((char *)orig_name, &st_orig) >= 0
1877 && mch_stat((char *)file_name, &st_undo) >= 0
Bram Moolenaar3b262392013-09-08 15:40:49 +02001878 && st_orig.st_uid != st_undo.st_uid
1879 && st_undo.st_uid != getuid())
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001880 {
1881 if (p_verbose > 0)
1882 {
1883 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001884 smsg(_("Not reading undo file, owner differs: %s"),
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001885 file_name);
1886 verbose_leave();
1887 }
1888 return;
1889 }
1890#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001891 }
1892 else
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001893 file_name = name;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001894
1895 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001896 {
1897 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001898 smsg(_("Reading undo file: %s"), file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001899 verbose_leave();
1900 }
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001901
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001902 fp = mch_fopen((char *)file_name, "r");
1903 if (fp == NULL)
1904 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001905 if (name != NULL || p_verbose > 0)
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00001906 semsg(_(e_cannot_open_undo_file_for_reading_str), file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001907 goto error;
1908 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001909 bi.bi_buf = curbuf;
1910 bi.bi_fp = fp;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001911
Bram Moolenaar9db58062010-05-29 20:33:07 +02001912 /*
1913 * Read the undo file header.
1914 */
1915 if (fread(magic_buf, UF_START_MAGIC_LEN, 1, fp) != 1
1916 || memcmp(magic_buf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001917 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00001918 semsg(_(e_not_an_undo_file_str), file_name);
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001919 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001920 }
1921 version = get2c(fp);
Bram Moolenaar69154f22010-07-18 21:42:34 +02001922 if (version == UF_VERSION_CRYPT)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001923 {
1924#ifdef FEAT_CRYPT
Bram Moolenaar56be9502010-06-06 14:20:26 +02001925 if (*curbuf->b_p_key == NUL)
1926 {
Bram Moolenaara9fa8c52023-01-02 18:10:04 +00001927 semsg(_(e_non_encrypted_file_has_encrypted_undo_file_str),
1928 file_name);
Bram Moolenaar56be9502010-06-06 14:20:26 +02001929 goto error;
1930 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001931 bi.bi_state = crypt_create_from_file(fp, curbuf->b_p_key);
1932 if (bi.bi_state == NULL)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001933 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00001934 semsg(_(e_undo_file_decryption_failed), file_name);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001935 goto error;
1936 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001937 if (crypt_whole_undofile(bi.bi_state->method_nr))
1938 {
1939 bi.bi_buffer = alloc(CRYPT_BUF_SIZE);
1940 if (bi.bi_buffer == NULL)
1941 {
1942 crypt_free_state(bi.bi_state);
1943 bi.bi_state = NULL;
1944 goto error;
1945 }
1946 bi.bi_avail = 0;
1947 bi.bi_used = 0;
1948 }
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001949#else
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00001950 semsg(_(e_undo_file_is_encrypted_str), file_name);
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001951 goto error;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001952#endif
1953 }
Bram Moolenaar69154f22010-07-18 21:42:34 +02001954 else if (version != UF_VERSION)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001955 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00001956 semsg(_(e_incompatible_undo_file_str), file_name);
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001957 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001958 }
1959
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001960 if (undo_read(&bi, read_hash, (size_t)UNDO_HASH_SIZE) == FAIL)
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001961 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001962 corruption_error("hash", file_name);
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001963 goto error;
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001964 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001965 line_count = (linenr_T)undo_read_4c(&bi);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001966 if (memcmp(hash, read_hash, UNDO_HASH_SIZE) != 0
1967 || line_count != curbuf->b_ml.ml_line_count)
1968 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001969 if (p_verbose > 0 || name != NULL)
1970 {
Bram Moolenaar504a8212010-05-30 17:17:42 +02001971 if (name == NULL)
1972 verbose_enter();
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001973 give_warning((char_u *)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001974 _("File contents changed, cannot use undo info"), TRUE);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001975 if (name == NULL)
1976 verbose_leave();
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001977 }
1978 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001979 }
1980
Bram Moolenaare38eab22019-12-05 21:50:01 +01001981 // Read undo data for "U" command.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001982 str_len = undo_read_4c(&bi);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001983 if (str_len < 0)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001984 goto error;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001985 if (str_len > 0)
Bram Moolenaarccae4672019-01-04 15:09:57 +01001986 {
1987 line_ptr.ul_line = read_string_decrypt(&bi, str_len);
1988 line_ptr.ul_len = str_len + 1;
1989 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001990 line_lnum = (linenr_T)undo_read_4c(&bi);
1991 line_colnr = (colnr_T)undo_read_4c(&bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001992 if (line_lnum < 0 || line_colnr < 0)
1993 {
1994 corruption_error("line lnum/col", file_name);
1995 goto error;
1996 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001997
Bram Moolenaare38eab22019-12-05 21:50:01 +01001998 // Begin general undo data
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001999 old_header_seq = undo_read_4c(&bi);
2000 new_header_seq = undo_read_4c(&bi);
2001 cur_header_seq = undo_read_4c(&bi);
2002 num_head = undo_read_4c(&bi);
2003 seq_last = undo_read_4c(&bi);
2004 seq_cur = undo_read_4c(&bi);
2005 seq_time = undo_read_time(&bi);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002006
Bram Moolenaare38eab22019-12-05 21:50:01 +01002007 // Optional header fields.
Bram Moolenaar69154f22010-07-18 21:42:34 +02002008 for (;;)
2009 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002010 int len = undo_read_byte(&bi);
Bram Moolenaar69154f22010-07-18 21:42:34 +02002011 int what;
Bram Moolenaara800b422010-06-27 01:15:55 +02002012
Bram Moolenaar69154f22010-07-18 21:42:34 +02002013 if (len == 0 || len == EOF)
2014 break;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002015 what = undo_read_byte(&bi);
Bram Moolenaar69154f22010-07-18 21:42:34 +02002016 switch (what)
2017 {
2018 case UF_LAST_SAVE_NR:
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002019 last_save_nr = undo_read_4c(&bi);
Bram Moolenaara800b422010-06-27 01:15:55 +02002020 break;
Bram Moolenaar69154f22010-07-18 21:42:34 +02002021 default:
Bram Moolenaare38eab22019-12-05 21:50:01 +01002022 // field not supported, skip
Bram Moolenaar69154f22010-07-18 21:42:34 +02002023 while (--len >= 0)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002024 (void)undo_read_byte(&bi);
Bram Moolenaara800b422010-06-27 01:15:55 +02002025 }
Bram Moolenaar69154f22010-07-18 21:42:34 +02002026 }
Bram Moolenaara800b422010-06-27 01:15:55 +02002027
Bram Moolenaare38eab22019-12-05 21:50:01 +01002028 // uhp_table will store the freshly created undo headers we allocate
2029 // until we insert them into curbuf. The table remains sorted by the
2030 // sequence numbers of the headers.
2031 // When there are no headers uhp_table is NULL.
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002032 if (num_head > 0)
2033 {
Bram Moolenaar3eb16372017-02-26 18:11:36 +01002034 if (num_head < LONG_MAX / (long)sizeof(u_header_T *))
Bram Moolenaarc799fe22019-05-28 23:08:19 +02002035 uhp_table = U_ALLOC_LINE(num_head * sizeof(u_header_T *));
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002036 if (uhp_table == NULL)
2037 goto error;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002038 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002039
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002040 while ((c = undo_read_2c(&bi)) == UF_HEADER_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002041 {
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02002042 if (num_read_uhps >= num_head)
2043 {
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02002044 corruption_error("num_head too small", file_name);
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02002045 goto error;
2046 }
2047
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002048 uhp = unserialize_uhp(&bi, file_name);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02002049 if (uhp == NULL)
2050 goto error;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02002051 uhp_table[num_read_uhps++] = uhp;
2052 }
2053
2054 if (num_read_uhps != num_head)
2055 {
2056 corruption_error("num_head", file_name);
2057 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002058 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02002059 if (c != UF_HEADER_END_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002060 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02002061 corruption_error("end marker", file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002062 goto error;
2063 }
2064
Bram Moolenaar9db58062010-05-29 20:33:07 +02002065#ifdef U_DEBUG
Bram Moolenaarc799fe22019-05-28 23:08:19 +02002066 uhp_table_used = alloc_clear(sizeof(int) * num_head + 1);
Bram Moolenaar9db58062010-05-29 20:33:07 +02002067# define SET_FLAG(j) ++uhp_table_used[j]
2068#else
2069# define SET_FLAG(j)
2070#endif
2071
Bram Moolenaare38eab22019-12-05 21:50:01 +01002072 // We have put all of the headers into a table. Now we iterate through the
2073 // table and swizzle each sequence number we have stored in uh_*_seq into
2074 // a pointer corresponding to the header with that sequence number.
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002075 for (i = 0; i < num_head; i++)
2076 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002077 uhp = uhp_table[i];
2078 if (uhp == NULL)
2079 continue;
2080 for (j = 0; j < num_head; j++)
2081 if (uhp_table[j] != NULL && i != j
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002082 && uhp_table[i]->uh_seq == uhp_table[j]->uh_seq)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002083 {
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02002084 corruption_error("duplicate uh_seq", file_name);
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002085 goto error;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02002086 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002087 for (j = 0; j < num_head; j++)
2088 if (uhp_table[j] != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002089 && uhp_table[j]->uh_seq == uhp->uh_next.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002090 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002091 uhp->uh_next.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02002092 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002093 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002094 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002095 for (j = 0; j < num_head; j++)
2096 if (uhp_table[j] != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002097 && uhp_table[j]->uh_seq == uhp->uh_prev.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002098 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002099 uhp->uh_prev.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02002100 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002101 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002102 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002103 for (j = 0; j < num_head; j++)
2104 if (uhp_table[j] != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002105 && uhp_table[j]->uh_seq == uhp->uh_alt_next.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002106 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002107 uhp->uh_alt_next.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02002108 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002109 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002110 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002111 for (j = 0; j < num_head; j++)
2112 if (uhp_table[j] != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002113 && uhp_table[j]->uh_seq == uhp->uh_alt_prev.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002114 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002115 uhp->uh_alt_prev.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02002116 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002117 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002118 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002119 if (old_header_seq > 0 && old_idx < 0 && uhp->uh_seq == old_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002120 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002121 old_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002122 SET_FLAG(i);
2123 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002124 if (new_header_seq > 0 && new_idx < 0 && uhp->uh_seq == new_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002125 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002126 new_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002127 SET_FLAG(i);
2128 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002129 if (cur_header_seq > 0 && cur_idx < 0 && uhp->uh_seq == cur_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002130 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002131 cur_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002132 SET_FLAG(i);
2133 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002134 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02002135
Bram Moolenaare38eab22019-12-05 21:50:01 +01002136 // Now that we have read the undo info successfully, free the current undo
2137 // info and use the info from the file.
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002138 u_blockfree(curbuf);
Bram Moolenaar9db58062010-05-29 20:33:07 +02002139 curbuf->b_u_oldhead = old_idx < 0 ? NULL : uhp_table[old_idx];
2140 curbuf->b_u_newhead = new_idx < 0 ? NULL : uhp_table[new_idx];
2141 curbuf->b_u_curhead = cur_idx < 0 ? NULL : uhp_table[cur_idx];
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002142 curbuf->b_u_line_ptr = line_ptr;
2143 curbuf->b_u_line_lnum = line_lnum;
2144 curbuf->b_u_line_colnr = line_colnr;
2145 curbuf->b_u_numhead = num_head;
2146 curbuf->b_u_seq_last = seq_last;
2147 curbuf->b_u_seq_cur = seq_cur;
Bram Moolenaara800b422010-06-27 01:15:55 +02002148 curbuf->b_u_time_cur = seq_time;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002149 curbuf->b_u_save_nr_last = last_save_nr;
Bram Moolenaardba01a02010-11-03 19:32:42 +01002150 curbuf->b_u_save_nr_cur = last_save_nr;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02002151
2152 curbuf->b_u_synced = TRUE;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002153 vim_free(uhp_table);
Bram Moolenaar9db58062010-05-29 20:33:07 +02002154
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002155#ifdef U_DEBUG
Bram Moolenaar9db58062010-05-29 20:33:07 +02002156 for (i = 0; i < num_head; ++i)
2157 if (uhp_table_used[i] == 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002158 semsg("uhp_table entry %ld not used, leaking memory", i);
Bram Moolenaar9db58062010-05-29 20:33:07 +02002159 vim_free(uhp_table_used);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002160 u_check(TRUE);
2161#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02002162
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002163 if (name != NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002164 smsg(_("Finished reading undo file %s"), file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002165 goto theend;
2166
2167error:
Bram Moolenaarccae4672019-01-04 15:09:57 +01002168 vim_free(line_ptr.ul_line);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002169 if (uhp_table != NULL)
2170 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002171 for (i = 0; i < num_read_uhps; i++)
2172 if (uhp_table[i] != NULL)
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02002173 u_free_uhp(uhp_table[i]);
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002174 vim_free(uhp_table);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002175 }
2176
2177theend:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002178#ifdef FEAT_CRYPT
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002179 if (bi.bi_state != NULL)
2180 crypt_free_state(bi.bi_state);
2181 vim_free(bi.bi_buffer);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002182#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002183 if (fp != NULL)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002184 fclose(fp);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002185 if (file_name != name)
2186 vim_free(file_name);
2187 return;
2188}
2189
Bram Moolenaare38eab22019-12-05 21:50:01 +01002190#endif // FEAT_PERSISTENT_UNDO
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002191
2192
Bram Moolenaar071d4272004-06-13 20:20:40 +00002193/*
2194 * If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible).
2195 * If 'cpoptions' does not contain 'u': Always undo.
2196 */
2197 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002198u_undo(int count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002199{
2200 /*
2201 * If we get an undo command while executing a macro, we behave like the
2202 * original vi. If this happens twice in one macro the result will not
2203 * be compatible.
2204 */
2205 if (curbuf->b_u_synced == FALSE)
2206 {
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002207 u_sync(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002208 count = 1;
2209 }
2210
2211 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
2212 undo_undoes = TRUE;
2213 else
2214 undo_undoes = !undo_undoes;
2215 u_doit(count);
2216}
2217
2218/*
2219 * If 'cpoptions' contains 'u': Repeat the previous undo or redo.
2220 * If 'cpoptions' does not contain 'u': Always redo.
2221 */
2222 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002223u_redo(int count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002224{
2225 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
2226 undo_undoes = FALSE;
2227 u_doit(count);
2228}
2229
2230/*
2231 * Undo or redo, depending on 'undo_undoes', 'count' times.
2232 */
2233 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002234u_doit(int startcount)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002235{
Bram Moolenaarca003e12006-03-17 23:19:38 +00002236 int count = startcount;
2237
Bram Moolenaar8ada17c2006-01-19 22:16:24 +00002238 if (!undo_allowed())
Bram Moolenaar071d4272004-06-13 20:20:40 +00002239 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002240
2241 u_newcount = 0;
2242 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00002243 if (curbuf->b_ml.ml_flags & ML_EMPTY)
2244 u_oldcount = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002245 while (count--)
2246 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002247 // Do the change warning now, so that it triggers FileChangedRO when
2248 // needed. This may cause the file to be reloaded, that must happen
2249 // before we do anything, because it may change curbuf->b_u_curhead
2250 // and more.
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002251 change_warning(0);
2252
Bram Moolenaar071d4272004-06-13 20:20:40 +00002253 if (undo_undoes)
2254 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002255 if (curbuf->b_u_curhead == NULL) // first undo
Bram Moolenaar071d4272004-06-13 20:20:40 +00002256 curbuf->b_u_curhead = curbuf->b_u_newhead;
Bram Moolenaare38eab22019-12-05 21:50:01 +01002257 else if (get_undolevel() > 0) // multi level undo
2258 // get next undo
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002259 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaare38eab22019-12-05 21:50:01 +01002260 // nothing to undo
Bram Moolenaar071d4272004-06-13 20:20:40 +00002261 if (curbuf->b_u_numhead == 0 || curbuf->b_u_curhead == NULL)
2262 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002263 // stick curbuf->b_u_curhead at end
Bram Moolenaar071d4272004-06-13 20:20:40 +00002264 curbuf->b_u_curhead = curbuf->b_u_oldhead;
2265 beep_flush();
Bram Moolenaarca003e12006-03-17 23:19:38 +00002266 if (count == startcount - 1)
2267 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002268 msg(_("Already at oldest change"));
Bram Moolenaarca003e12006-03-17 23:19:38 +00002269 return;
2270 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002271 break;
2272 }
2273
Bram Moolenaarca003e12006-03-17 23:19:38 +00002274 u_undoredo(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002275 }
2276 else
2277 {
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +01002278 if (curbuf->b_u_curhead == NULL || get_undolevel() <= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002279 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002280 beep_flush(); // nothing to redo
Bram Moolenaarca003e12006-03-17 23:19:38 +00002281 if (count == startcount - 1)
2282 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002283 msg(_("Already at newest change"));
Bram Moolenaarca003e12006-03-17 23:19:38 +00002284 return;
2285 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002286 break;
2287 }
2288
Bram Moolenaarca003e12006-03-17 23:19:38 +00002289 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002290
Bram Moolenaare38eab22019-12-05 21:50:01 +01002291 // Advance for next redo. Set "newhead" when at the end of the
2292 // redoable changes.
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002293 if (curbuf->b_u_curhead->uh_prev.ptr == NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002294 curbuf->b_u_newhead = curbuf->b_u_curhead;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002295 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002296 }
2297 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00002298 u_undo_end(undo_undoes, FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002299}
2300
Bram Moolenaar1e607892006-03-13 22:15:53 +00002301/*
2302 * Undo or redo over the timeline.
2303 * When "step" is negative go back in time, otherwise goes forward in time.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002304 * When "sec" is FALSE make "step" steps, when "sec" is TRUE use "step" as
2305 * seconds.
Bram Moolenaar730cde92010-06-27 05:18:54 +02002306 * When "file" is TRUE use "step" as a number of file writes.
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002307 * When "absolute" is TRUE use "step" as the sequence number to jump to.
2308 * "sec" must be FALSE then.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002309 */
2310 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002311undo_time(
2312 long step,
2313 int sec,
2314 int file,
2315 int absolute)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002316{
2317 long target;
2318 long closest;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002319 long closest_start;
2320 long closest_seq = 0;
2321 long val;
Bram Moolenaarce46d932018-01-30 22:46:06 +01002322 u_header_T *uhp = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002323 u_header_T *last;
2324 int mark;
Bram Moolenaar1f3601e2019-04-26 20:33:00 +02002325 int nomark = 0; // shut up compiler
Bram Moolenaar1e607892006-03-13 22:15:53 +00002326 int round;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002327 int dosec = sec;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002328 int dofile = file;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002329 int above = FALSE;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002330 int did_undo = TRUE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002331
Bram Moolenaar338f1fc2022-05-26 15:56:23 +01002332 if (text_locked())
2333 {
2334 text_locked_msg();
2335 return;
2336 }
2337
Bram Moolenaare38eab22019-12-05 21:50:01 +01002338 // First make sure the current undoable change is synced.
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00002339 if (curbuf->b_u_synced == FALSE)
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002340 u_sync(TRUE);
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00002341
Bram Moolenaar1e607892006-03-13 22:15:53 +00002342 u_newcount = 0;
2343 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00002344 if (curbuf->b_ml.ml_flags & ML_EMPTY)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002345 u_oldcount = -1;
2346
Bram Moolenaare38eab22019-12-05 21:50:01 +01002347 // "target" is the node below which we want to be.
2348 // Init "closest" to a value we can't reach.
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002349 if (absolute)
2350 {
Bram Moolenaarce46d932018-01-30 22:46:06 +01002351 target = step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002352 closest = -1;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002353 }
Bram Moolenaarca003e12006-03-17 23:19:38 +00002354 else
Bram Moolenaar1e607892006-03-13 22:15:53 +00002355 {
Bram Moolenaar730cde92010-06-27 05:18:54 +02002356 if (dosec)
Bram Moolenaarcbd4de42017-01-07 16:14:57 +01002357 target = (long)(curbuf->b_u_time_cur) + step;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002358 else if (dofile)
2359 {
2360 if (step < 0)
2361 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002362 // Going back to a previous write. If there were changes after
2363 // the last write, count that as moving one file-write, so
2364 // that ":earlier 1f" undoes all changes since the last save.
Bram Moolenaar730cde92010-06-27 05:18:54 +02002365 uhp = curbuf->b_u_curhead;
2366 if (uhp != NULL)
2367 uhp = uhp->uh_next.ptr;
2368 else
2369 uhp = curbuf->b_u_newhead;
2370 if (uhp != NULL && uhp->uh_save_nr != 0)
Bram Moolenaare38eab22019-12-05 21:50:01 +01002371 // "uh_save_nr" was set in the last block, that means
2372 // there were no changes since the last write
Bram Moolenaar730cde92010-06-27 05:18:54 +02002373 target = curbuf->b_u_save_nr_cur + step;
2374 else
Bram Moolenaare38eab22019-12-05 21:50:01 +01002375 // count the changes since the last write as one step
Bram Moolenaar730cde92010-06-27 05:18:54 +02002376 target = curbuf->b_u_save_nr_cur + step + 1;
2377 if (target <= 0)
Bram Moolenaare38eab22019-12-05 21:50:01 +01002378 // Go to before first write: before the oldest change. Use
2379 // the sequence number for that.
Bram Moolenaar730cde92010-06-27 05:18:54 +02002380 dofile = FALSE;
2381 }
2382 else
2383 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002384 // Moving forward to a newer write.
Bram Moolenaar730cde92010-06-27 05:18:54 +02002385 target = curbuf->b_u_save_nr_cur + step;
2386 if (target > curbuf->b_u_save_nr_last)
2387 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002388 // Go to after last write: after the latest change. Use
2389 // the sequence number for that.
Bram Moolenaar730cde92010-06-27 05:18:54 +02002390 target = curbuf->b_u_seq_last + 1;
2391 dofile = FALSE;
2392 }
2393 }
2394 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002395 else
2396 target = curbuf->b_u_seq_cur + step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002397 if (step < 0)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002398 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002399 if (target < 0)
2400 target = 0;
2401 closest = -1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002402 }
2403 else
2404 {
Bram Moolenaar730cde92010-06-27 05:18:54 +02002405 if (dosec)
Bram Moolenaarcbd4de42017-01-07 16:14:57 +01002406 closest = (long)(vim_time() + 1);
Bram Moolenaar730cde92010-06-27 05:18:54 +02002407 else if (dofile)
2408 closest = curbuf->b_u_save_nr_last + 2;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002409 else
2410 closest = curbuf->b_u_seq_last + 2;
2411 if (target >= closest)
2412 target = closest - 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002413 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002414 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002415 closest_start = closest;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002416 closest_seq = curbuf->b_u_seq_cur;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002417
Bram Moolenaare38eab22019-12-05 21:50:01 +01002418 // When "target" is 0; Back to origin.
Bram Moolenaarce46d932018-01-30 22:46:06 +01002419 if (target == 0)
Bram Moolenaar059fd012018-01-31 14:25:53 +01002420 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002421 mark = lastmark; // avoid that GCC complains
Bram Moolenaar059fd012018-01-31 14:25:53 +01002422 goto target_zero;
2423 }
Bram Moolenaarce46d932018-01-30 22:46:06 +01002424
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002425 /*
2426 * May do this twice:
Bram Moolenaar1e607892006-03-13 22:15:53 +00002427 * 1. Search for "target", update "closest" to the best match found.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002428 * 2. If "target" not found search for "closest".
2429 *
2430 * When using the closest time we use the sequence number in the second
2431 * round, because there may be several entries with the same time.
2432 */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002433 for (round = 1; round <= 2; ++round)
2434 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002435 // Find the path from the current state to where we want to go. The
2436 // desired state can be anywhere in the undo tree, need to go all over
2437 // it. We put "nomark" in uh_walk where we have been without success,
2438 // "mark" where it could possibly be.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002439 mark = ++lastmark;
2440 nomark = ++lastmark;
2441
Bram Moolenaare38eab22019-12-05 21:50:01 +01002442 if (curbuf->b_u_curhead == NULL) // at leaf of the tree
Bram Moolenaar1e607892006-03-13 22:15:53 +00002443 uhp = curbuf->b_u_newhead;
2444 else
2445 uhp = curbuf->b_u_curhead;
2446
2447 while (uhp != NULL)
2448 {
2449 uhp->uh_walk = mark;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002450 if (dosec)
Bram Moolenaarcbd4de42017-01-07 16:14:57 +01002451 val = (long)(uhp->uh_time);
Bram Moolenaar730cde92010-06-27 05:18:54 +02002452 else if (dofile)
2453 val = uhp->uh_save_nr;
2454 else
2455 val = uhp->uh_seq;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002456
Bram Moolenaar730cde92010-06-27 05:18:54 +02002457 if (round == 1 && !(dofile && val == 0))
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002458 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002459 // Remember the header that is closest to the target.
2460 // It must be at least in the right direction (checked with
2461 // "b_u_seq_cur"). When the timestamp is equal find the
2462 // highest/lowest sequence number.
Bram Moolenaarca003e12006-03-17 23:19:38 +00002463 if ((step < 0 ? uhp->uh_seq <= curbuf->b_u_seq_cur
2464 : uhp->uh_seq > curbuf->b_u_seq_cur)
2465 && ((dosec && val == closest)
2466 ? (step < 0
2467 ? uhp->uh_seq < closest_seq
2468 : uhp->uh_seq > closest_seq)
2469 : closest == closest_start
2470 || (val > target
2471 ? (closest > target
2472 ? val - target <= closest - target
2473 : val - target <= target - closest)
2474 : (closest > target
2475 ? target - val <= closest - target
2476 : target - val <= target - closest))))
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002477 {
2478 closest = val;
2479 closest_seq = uhp->uh_seq;
2480 }
2481 }
2482
Bram Moolenaare38eab22019-12-05 21:50:01 +01002483 // Quit searching when we found a match. But when searching for a
2484 // time we need to continue looking for the best uh_seq.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002485 if (target == val && !dosec)
Bram Moolenaar730cde92010-06-27 05:18:54 +02002486 {
2487 target = uhp->uh_seq;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002488 break;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002489 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002490
Bram Moolenaare38eab22019-12-05 21:50:01 +01002491 // go down in the tree if we haven't been there
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002492 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
2493 && uhp->uh_prev.ptr->uh_walk != mark)
2494 uhp = uhp->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002495
Bram Moolenaare38eab22019-12-05 21:50:01 +01002496 // go to alternate branch if we haven't been there
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002497 else if (uhp->uh_alt_next.ptr != NULL
2498 && uhp->uh_alt_next.ptr->uh_walk != nomark
2499 && uhp->uh_alt_next.ptr->uh_walk != mark)
2500 uhp = uhp->uh_alt_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002501
Bram Moolenaare38eab22019-12-05 21:50:01 +01002502 // go up in the tree if we haven't been there and we are at the
2503 // start of alternate branches
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002504 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
2505 && uhp->uh_next.ptr->uh_walk != nomark
2506 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002507 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002508 // If still at the start we don't go through this change.
Bram Moolenaardb552d602006-03-23 22:59:57 +00002509 if (uhp == curbuf->b_u_curhead)
2510 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002511 uhp = uhp->uh_next.ptr;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002512 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002513
2514 else
2515 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002516 // need to backtrack; mark this node as useless
Bram Moolenaar1e607892006-03-13 22:15:53 +00002517 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002518 if (uhp->uh_alt_prev.ptr != NULL)
2519 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002520 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002521 uhp = uhp->uh_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002522 }
2523 }
2524
Bram Moolenaare38eab22019-12-05 21:50:01 +01002525 if (uhp != NULL) // found it
Bram Moolenaar1e607892006-03-13 22:15:53 +00002526 break;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002527
2528 if (absolute)
2529 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00002530 semsg(_(e_undo_number_nr_not_found), step);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002531 return;
2532 }
2533
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002534 if (closest == closest_start)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002535 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002536 if (step < 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002537 msg(_("Already at oldest change"));
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002538 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002539 msg(_("Already at newest change"));
Bram Moolenaar1e607892006-03-13 22:15:53 +00002540 return;
2541 }
2542
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002543 target = closest_seq;
2544 dosec = FALSE;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002545 dofile = FALSE;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002546 if (step < 0)
Bram Moolenaare38eab22019-12-05 21:50:01 +01002547 above = TRUE; // stop above the header
Bram Moolenaar1e607892006-03-13 22:15:53 +00002548 }
2549
Bram Moolenaar059fd012018-01-31 14:25:53 +01002550target_zero:
Bram Moolenaare38eab22019-12-05 21:50:01 +01002551 // If we found it: Follow the path to go to where we want to be.
Bram Moolenaarce46d932018-01-30 22:46:06 +01002552 if (uhp != NULL || target == 0)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002553 {
2554 /*
2555 * First go up the tree as much as needed.
2556 */
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002557 while (!got_int)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002558 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002559 // Do the change warning now, for the same reason as above.
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002560 change_warning(0);
2561
Bram Moolenaar1e607892006-03-13 22:15:53 +00002562 uhp = curbuf->b_u_curhead;
2563 if (uhp == NULL)
2564 uhp = curbuf->b_u_newhead;
2565 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002566 uhp = uhp->uh_next.ptr;
Bram Moolenaarce46d932018-01-30 22:46:06 +01002567 if (uhp == NULL || (target > 0 && uhp->uh_walk != mark)
Bram Moolenaarca003e12006-03-17 23:19:38 +00002568 || (uhp->uh_seq == target && !above))
Bram Moolenaar1e607892006-03-13 22:15:53 +00002569 break;
2570 curbuf->b_u_curhead = uhp;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002571 u_undoredo(TRUE);
Bram Moolenaarce46d932018-01-30 22:46:06 +01002572 if (target > 0)
Bram Moolenaare38eab22019-12-05 21:50:01 +01002573 uhp->uh_walk = nomark; // don't go back down here
Bram Moolenaar1e607892006-03-13 22:15:53 +00002574 }
2575
Bram Moolenaare38eab22019-12-05 21:50:01 +01002576 // When back to origin, redo is not needed.
Bram Moolenaarce46d932018-01-30 22:46:06 +01002577 if (target > 0)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002578 {
Bram Moolenaarce46d932018-01-30 22:46:06 +01002579 /*
2580 * And now go down the tree (redo), branching off where needed.
2581 */
2582 while (!got_int)
2583 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002584 // Do the change warning now, for the same reason as above.
Bram Moolenaarce46d932018-01-30 22:46:06 +01002585 change_warning(0);
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002586
Bram Moolenaarce46d932018-01-30 22:46:06 +01002587 uhp = curbuf->b_u_curhead;
2588 if (uhp == NULL)
2589 break;
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002590
Bram Moolenaare38eab22019-12-05 21:50:01 +01002591 // Go back to the first branch with a mark.
Bram Moolenaarce46d932018-01-30 22:46:06 +01002592 while (uhp->uh_alt_prev.ptr != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002593 && uhp->uh_alt_prev.ptr->uh_walk == mark)
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002594 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaarce46d932018-01-30 22:46:06 +01002595
Bram Moolenaare38eab22019-12-05 21:50:01 +01002596 // Find the last branch with a mark, that's the one.
Bram Moolenaarce46d932018-01-30 22:46:06 +01002597 last = uhp;
2598 while (last->uh_alt_next.ptr != NULL
2599 && last->uh_alt_next.ptr->uh_walk == mark)
2600 last = last->uh_alt_next.ptr;
2601 if (last != uhp)
2602 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002603 // Make the used branch the first entry in the list of
2604 // alternatives to make "u" and CTRL-R take this branch.
Bram Moolenaarce46d932018-01-30 22:46:06 +01002605 while (uhp->uh_alt_prev.ptr != NULL)
2606 uhp = uhp->uh_alt_prev.ptr;
2607 if (last->uh_alt_next.ptr != NULL)
2608 last->uh_alt_next.ptr->uh_alt_prev.ptr =
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002609 last->uh_alt_prev.ptr;
Bram Moolenaarce46d932018-01-30 22:46:06 +01002610 last->uh_alt_prev.ptr->uh_alt_next.ptr =
2611 last->uh_alt_next.ptr;
2612 last->uh_alt_prev.ptr = NULL;
2613 last->uh_alt_next.ptr = uhp;
2614 uhp->uh_alt_prev.ptr = last;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002615
Bram Moolenaarce46d932018-01-30 22:46:06 +01002616 if (curbuf->b_u_oldhead == uhp)
2617 curbuf->b_u_oldhead = last;
2618 uhp = last;
2619 if (uhp->uh_next.ptr != NULL)
2620 uhp->uh_next.ptr->uh_prev.ptr = uhp;
2621 }
2622 curbuf->b_u_curhead = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002623
Bram Moolenaarce46d932018-01-30 22:46:06 +01002624 if (uhp->uh_walk != mark)
Bram Moolenaare38eab22019-12-05 21:50:01 +01002625 break; // must have reached the target
Bram Moolenaar1e607892006-03-13 22:15:53 +00002626
Bram Moolenaare38eab22019-12-05 21:50:01 +01002627 // Stop when going backwards in time and didn't find the exact
2628 // header we were looking for.
Bram Moolenaarce46d932018-01-30 22:46:06 +01002629 if (uhp->uh_seq == target && above)
2630 {
2631 curbuf->b_u_seq_cur = target - 1;
2632 break;
2633 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002634
Bram Moolenaarce46d932018-01-30 22:46:06 +01002635 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002636
Bram Moolenaare38eab22019-12-05 21:50:01 +01002637 // Advance "curhead" to below the header we last used. If it
2638 // becomes NULL then we need to set "newhead" to this leaf.
Bram Moolenaarce46d932018-01-30 22:46:06 +01002639 if (uhp->uh_prev.ptr == NULL)
2640 curbuf->b_u_newhead = uhp;
2641 curbuf->b_u_curhead = uhp->uh_prev.ptr;
2642 did_undo = FALSE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002643
Bram Moolenaare38eab22019-12-05 21:50:01 +01002644 if (uhp->uh_seq == target) // found it!
Bram Moolenaarce46d932018-01-30 22:46:06 +01002645 break;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002646
Bram Moolenaarce46d932018-01-30 22:46:06 +01002647 uhp = uhp->uh_prev.ptr;
2648 if (uhp == NULL || uhp->uh_walk != mark)
2649 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002650 // Need to redo more but can't find it...
Bram Moolenaarce46d932018-01-30 22:46:06 +01002651 internal_error("undo_time()");
2652 break;
2653 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002654 }
2655 }
2656 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00002657 u_undo_end(did_undo, absolute);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002658}
2659
2660/*
2661 * u_undoredo: common code for undo and redo
2662 *
2663 * The lines in the file are replaced by the lines in the entry list at
2664 * curbuf->b_u_curhead. The replaced lines in the file are saved in the entry
2665 * list for the next undo/redo.
Bram Moolenaarca003e12006-03-17 23:19:38 +00002666 *
2667 * When "undo" is TRUE we go up in the tree, when FALSE we go down.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002668 */
2669 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002670u_undoredo(int undo)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002671{
Bram Moolenaarccae4672019-01-04 15:09:57 +01002672 undoline_T *newarray = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002673 linenr_T oldsize;
2674 linenr_T newsize;
2675 linenr_T top, bot;
2676 linenr_T lnum;
2677 linenr_T newlnum = MAXLNUM;
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002678 pos_T new_curpos = curwin->w_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002679 long i;
2680 u_entry_T *uep, *nuep;
2681 u_entry_T *newlist = NULL;
2682 int old_flags;
2683 int new_flags;
2684 pos_T namedm[NMARKS];
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002685 visualinfo_T visualinfo;
Bram Moolenaare38eab22019-12-05 21:50:01 +01002686 int empty_buffer; // buffer became empty
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002687 u_header_T *curhead = curbuf->b_u_curhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002688
Bram Moolenaare38eab22019-12-05 21:50:01 +01002689 // Don't want autocommands using the undo structures here, they are
2690 // invalid till the end.
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002691 block_autocmds();
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002692
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002693#ifdef U_DEBUG
2694 u_check(FALSE);
2695#endif
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002696 old_flags = curhead->uh_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002697 new_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
2698 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
2699 setpcmark();
2700
2701 /*
2702 * save marks before undo/redo
2703 */
2704 mch_memmove(namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002705 visualinfo = curbuf->b_visual;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002706 curbuf->b_op_start.lnum = curbuf->b_ml.ml_line_count;
2707 curbuf->b_op_start.col = 0;
2708 curbuf->b_op_end.lnum = 0;
2709 curbuf->b_op_end.col = 0;
2710
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002711 for (uep = curhead->uh_entry; uep != NULL; uep = nuep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002712 {
2713 top = uep->ue_top;
2714 bot = uep->ue_bot;
2715 if (bot == 0)
2716 bot = curbuf->b_ml.ml_line_count + 1;
2717 if (top > curbuf->b_ml.ml_line_count || top >= bot
2718 || bot > curbuf->b_ml.ml_line_count + 1)
2719 {
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002720 unblock_autocmds();
RestorerZ68ebcee2023-05-31 17:12:14 +01002721 iemsg(e_u_undo_line_numbers_wrong);
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002722 changed(); // don't want UNCHANGED now
Bram Moolenaar071d4272004-06-13 20:20:40 +00002723 return;
2724 }
2725
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002726 oldsize = bot - top - 1; // number of lines before undo
2727 newsize = uep->ue_size; // number of lines after undo
Bram Moolenaar071d4272004-06-13 20:20:40 +00002728
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002729 // Decide about the cursor position, depending on what text changed.
2730 // Don't set it yet, it may be invalid if lines are going to be added.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002731 if (top < newlnum)
2732 {
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002733 // If the saved cursor is somewhere in this undo block, move it to
2734 // the remembered position. Makes "gwap" put the cursor back
2735 // where it was.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002736 lnum = curhead->uh_cursor.lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002737 if (lnum >= top && lnum <= top + newsize + 1)
2738 {
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002739 new_curpos = curhead->uh_cursor;
2740 newlnum = new_curpos.lnum - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002741 }
2742 else
2743 {
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002744 // Use the first line that actually changed. Avoids that
2745 // undoing auto-formatting puts the cursor in the previous
2746 // line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002747 for (i = 0; i < newsize && i < oldsize; ++i)
Bram Moolenaarccae4672019-01-04 15:09:57 +01002748 {
2749 char_u *p = ml_get(top + 1 + i);
2750
2751 if (curbuf->b_ml.ml_line_len != uep->ue_array[i].ul_len
Bram Moolenaar964b3742019-05-24 18:54:09 +02002752 || memcmp(uep->ue_array[i].ul_line, p,
2753 curbuf->b_ml.ml_line_len) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002754 break;
Bram Moolenaarccae4672019-01-04 15:09:57 +01002755 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002756 if (i == newsize && newlnum == MAXLNUM && uep->ue_next == NULL)
2757 {
2758 newlnum = top;
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002759 new_curpos.lnum = newlnum + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002760 }
2761 else if (i < newsize)
2762 {
2763 newlnum = top + i;
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002764 new_curpos.lnum = newlnum + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002765 }
2766 }
2767 }
2768
2769 empty_buffer = FALSE;
2770
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002771 /*
2772 * Delete the lines between top and bot and save them in newarray.
2773 */
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002774 if (oldsize > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002775 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02002776 if ((newarray = U_ALLOC_LINE(sizeof(undoline_T) * oldsize)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002777 {
Bram Moolenaarccae4672019-01-04 15:09:57 +01002778 do_outofmem_msg((long_u)(sizeof(undoline_T) * oldsize));
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002779
2780 // We have messed up the entry list, repair is impossible.
2781 // we have to free the rest of the list.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002782 while (uep != NULL)
2783 {
2784 nuep = uep->ue_next;
2785 u_freeentry(uep, uep->ue_size);
2786 uep = nuep;
2787 }
2788 break;
2789 }
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002790 // delete backwards, it goes faster in most cases
Bram Moolenaar071d4272004-06-13 20:20:40 +00002791 for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum)
2792 {
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002793 // what can we do when we run out of memory?
Bram Moolenaarccae4672019-01-04 15:09:57 +01002794 if (u_save_line(&newarray[i], lnum) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002795 do_outofmem_msg((long_u)0);
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002796 // remember we deleted the last line in the buffer, and a
2797 // dummy empty line will be inserted
Bram Moolenaar071d4272004-06-13 20:20:40 +00002798 if (curbuf->b_ml.ml_line_count == 1)
2799 empty_buffer = TRUE;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002800 ml_delete_flags(lnum, ML_DEL_UNDO);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002801 }
2802 }
Bram Moolenaar8d343302005-07-12 22:46:17 +00002803 else
2804 newarray = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002805
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002806 // make sure the cursor is on a valid line after the deletions
2807 check_cursor_lnum();
2808
2809 /*
2810 * Insert the lines in u_array between top and bot.
2811 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002812 if (newsize)
2813 {
2814 for (lnum = top, i = 0; i < newsize; ++i, ++lnum)
2815 {
Bram Moolenaarccae4672019-01-04 15:09:57 +01002816 // If the file is empty, there is an empty line 1 that we
2817 // should get rid of, by replacing it with the new line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002818 if (empty_buffer && lnum == 0)
Bram Moolenaar964b3742019-05-24 18:54:09 +02002819 ml_replace_len((linenr_T)1, uep->ue_array[i].ul_line,
2820 uep->ue_array[i].ul_len, TRUE, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002821 else
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002822 ml_append_flags(lnum, uep->ue_array[i].ul_line,
2823 (colnr_T)uep->ue_array[i].ul_len, ML_APPEND_UNDO);
Bram Moolenaarccae4672019-01-04 15:09:57 +01002824 vim_free(uep->ue_array[i].ul_line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002825 }
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002826 vim_free((char_u *)uep->ue_array);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002827 }
2828
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002829 // adjust marks
Bram Moolenaar071d4272004-06-13 20:20:40 +00002830 if (oldsize != newsize)
2831 {
2832 mark_adjust(top + 1, top + oldsize, (long)MAXLNUM,
2833 (long)newsize - (long)oldsize);
2834 if (curbuf->b_op_start.lnum > top + oldsize)
2835 curbuf->b_op_start.lnum += newsize - oldsize;
2836 if (curbuf->b_op_end.lnum > top + oldsize)
2837 curbuf->b_op_end.lnum += newsize - oldsize;
2838 }
Bram Moolenaar55737c22022-02-14 14:51:22 +00002839 if (oldsize > 0 || newsize > 0)
Bram Moolenaar26f09ea2022-09-27 16:29:38 +01002840 {
Bram Moolenaar55737c22022-02-14 14:51:22 +00002841 changed_lines(top + 1, 0, bot, newsize - oldsize);
Bram Moolenaar26f09ea2022-09-27 16:29:38 +01002842#ifdef FEAT_SPELL
2843 // When text has been changed, possibly the start of the next line
2844 // may have SpellCap that should be removed or it needs to be
2845 // displayed. Schedule the next line for redrawing just in case.
2846 if (spell_check_window(curwin) && bot <= curbuf->b_ml.ml_line_count)
2847 redrawWinline(curwin, bot);
2848#endif
2849 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002850
LemonBoy82444ce2022-05-12 15:39:31 +01002851 // Set the '[ mark.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002852 if (top + 1 < curbuf->b_op_start.lnum)
2853 curbuf->b_op_start.lnum = top + 1;
LemonBoy82444ce2022-05-12 15:39:31 +01002854 // Set the '] mark.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002855 if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum)
2856 curbuf->b_op_end.lnum = top + 1;
2857 else if (top + newsize > curbuf->b_op_end.lnum)
2858 curbuf->b_op_end.lnum = top + newsize;
2859
2860 u_newcount += newsize;
2861 u_oldcount += oldsize;
2862 uep->ue_size = oldsize;
2863 uep->ue_array = newarray;
2864 uep->ue_bot = top + newsize + 1;
2865
2866 /*
2867 * insert this entry in front of the new entry list
2868 */
2869 nuep = uep->ue_next;
2870 uep->ue_next = newlist;
2871 newlist = uep;
2872 }
2873
LemonBoy82444ce2022-05-12 15:39:31 +01002874 // Ensure the '[ and '] marks are within bounds.
2875 if (curbuf->b_op_start.lnum > curbuf->b_ml.ml_line_count)
2876 curbuf->b_op_start.lnum = curbuf->b_ml.ml_line_count;
2877 if (curbuf->b_op_end.lnum > curbuf->b_ml.ml_line_count)
2878 curbuf->b_op_end.lnum = curbuf->b_ml.ml_line_count;
2879
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002880 // Set the cursor to the desired position. Check that the line is valid.
2881 curwin->w_cursor = new_curpos;
2882 check_cursor_lnum();
2883
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002884 curhead->uh_entry = newlist;
2885 curhead->uh_flags = new_flags;
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01002886 if ((old_flags & UH_EMPTYBUF) && BUFEMPTY())
Bram Moolenaar071d4272004-06-13 20:20:40 +00002887 curbuf->b_ml.ml_flags |= ML_EMPTY;
2888 if (old_flags & UH_CHANGED)
2889 changed();
2890 else
Bram Moolenaar009b2592004-10-24 19:18:58 +00002891#ifdef FEAT_NETBEANS_INTG
Bram Moolenaare38eab22019-12-05 21:50:01 +01002892 // per netbeans undo rules, keep it as modified
Bram Moolenaar009b2592004-10-24 19:18:58 +00002893 if (!isNetbeansModified(curbuf))
2894#endif
Bram Moolenaarc024b462019-06-08 18:07:21 +02002895 unchanged(curbuf, FALSE, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002896
2897 /*
2898 * restore marks from before undo/redo
2899 */
2900 for (i = 0; i < NMARKS; ++i)
Bram Moolenaarf65aad52015-02-17 13:43:40 +01002901 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002902 if (curhead->uh_namedm[i].lnum != 0)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002903 curbuf->b_namedm[i] = curhead->uh_namedm[i];
Bram Moolenaarf65aad52015-02-17 13:43:40 +01002904 if (namedm[i].lnum != 0)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002905 curhead->uh_namedm[i] = namedm[i];
Bram Moolenaarf65aad52015-02-17 13:43:40 +01002906 else
2907 curhead->uh_namedm[i].lnum = 0;
2908 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002909 if (curhead->uh_visual.vi_start.lnum != 0)
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002910 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002911 curbuf->b_visual = curhead->uh_visual;
2912 curhead->uh_visual = visualinfo;
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002913 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002914
2915 /*
2916 * If the cursor is only off by one line, put it at the same position as
2917 * before starting the change (for the "o" command).
2918 * Otherwise the cursor should go to the first undone line.
2919 */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002920 if (curhead->uh_cursor.lnum + 1 == curwin->w_cursor.lnum
Bram Moolenaar071d4272004-06-13 20:20:40 +00002921 && curwin->w_cursor.lnum > 1)
2922 --curwin->w_cursor.lnum;
Bram Moolenaar0390ded2010-08-07 12:54:12 +02002923 if (curwin->w_cursor.lnum <= curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002924 {
Bram Moolenaar0390ded2010-08-07 12:54:12 +02002925 if (curhead->uh_cursor.lnum == curwin->w_cursor.lnum)
2926 {
2927 curwin->w_cursor.col = curhead->uh_cursor.col;
Bram Moolenaar0390ded2010-08-07 12:54:12 +02002928 if (virtual_active() && curhead->uh_cursor_vcol >= 0)
2929 coladvance((colnr_T)curhead->uh_cursor_vcol);
2930 else
2931 curwin->w_cursor.coladd = 0;
Bram Moolenaar0390ded2010-08-07 12:54:12 +02002932 }
2933 else
2934 beginline(BL_SOL | BL_FIX);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002935 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002936 else
2937 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002938 // We get here with the current cursor line being past the end (eg
2939 // after adding lines at the end of the file, and then undoing it).
2940 // check_cursor() will move the cursor to the last line. Move it to
2941 // the first column here.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002942 curwin->w_cursor.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002943 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002944 }
2945
Bram Moolenaare38eab22019-12-05 21:50:01 +01002946 // Make sure the cursor is on an existing line and column.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002947 check_cursor();
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002948
Bram Moolenaare38eab22019-12-05 21:50:01 +01002949 // Remember where we are for "g-" and ":earlier 10s".
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002950 curbuf->b_u_seq_cur = curhead->uh_seq;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002951 if (undo)
Bram Moolenaar80eaddd2017-11-11 23:37:08 +01002952 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002953 // We are below the previous undo. However, to make ":earlier 1s"
2954 // work we compute this as being just above the just undone change.
Bram Moolenaar80eaddd2017-11-11 23:37:08 +01002955 if (curhead->uh_next.ptr != NULL)
2956 curbuf->b_u_seq_cur = curhead->uh_next.ptr->uh_seq;
2957 else
2958 curbuf->b_u_seq_cur = 0;
2959 }
Bram Moolenaarca003e12006-03-17 23:19:38 +00002960
Bram Moolenaare38eab22019-12-05 21:50:01 +01002961 // Remember where we are for ":earlier 1f" and ":later 1f".
Bram Moolenaar730cde92010-06-27 05:18:54 +02002962 if (curhead->uh_save_nr != 0)
2963 {
2964 if (undo)
2965 curbuf->b_u_save_nr_cur = curhead->uh_save_nr - 1;
2966 else
2967 curbuf->b_u_save_nr_cur = curhead->uh_save_nr;
2968 }
2969
Bram Moolenaare38eab22019-12-05 21:50:01 +01002970 // The timestamp can be the same for multiple changes, just use the one of
2971 // the undone/redone change.
Bram Moolenaara800b422010-06-27 01:15:55 +02002972 curbuf->b_u_time_cur = curhead->uh_time;
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002973
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002974 unblock_autocmds();
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002975#ifdef U_DEBUG
2976 u_check(FALSE);
2977#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002978}
2979
2980/*
2981 * If we deleted or added lines, report the number of less/more lines.
2982 * Otherwise, report the number of changes (this may be incorrect
2983 * in some cases, but it's better than nothing).
2984 */
2985 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002986u_undo_end(
Bram Moolenaare38eab22019-12-05 21:50:01 +01002987 int did_undo, // just did an undo
2988 int absolute) // used ":undo N"
Bram Moolenaar071d4272004-06-13 20:20:40 +00002989{
Bram Moolenaar89d40322006-08-29 15:30:07 +00002990 char *msgstr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002991 u_header_T *uhp;
2992 char_u msgbuf[80];
Bram Moolenaar1e607892006-03-13 22:15:53 +00002993
Bram Moolenaar071d4272004-06-13 20:20:40 +00002994#ifdef FEAT_FOLDING
2995 if ((fdo_flags & FDO_UNDO) && KeyTyped)
2996 foldOpenCursor();
2997#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +00002998
Bram Moolenaare38eab22019-12-05 21:50:01 +01002999 if (global_busy // no messages now, wait until global is finished
3000 || !messaging()) // 'lazyredraw' set, don't do messages now
Bram Moolenaar1e607892006-03-13 22:15:53 +00003001 return;
3002
3003 if (curbuf->b_ml.ml_flags & ML_EMPTY)
3004 --u_newcount;
3005
3006 u_oldcount -= u_newcount;
3007 if (u_oldcount == -1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00003008 msgstr = N_("more line");
Bram Moolenaar1e607892006-03-13 22:15:53 +00003009 else if (u_oldcount < 0)
Bram Moolenaar89d40322006-08-29 15:30:07 +00003010 msgstr = N_("more lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00003011 else if (u_oldcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00003012 msgstr = N_("line less");
Bram Moolenaar1e607892006-03-13 22:15:53 +00003013 else if (u_oldcount > 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00003014 msgstr = N_("fewer lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00003015 else
3016 {
3017 u_oldcount = u_newcount;
3018 if (u_newcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00003019 msgstr = N_("change");
Bram Moolenaar1e607892006-03-13 22:15:53 +00003020 else
Bram Moolenaar89d40322006-08-29 15:30:07 +00003021 msgstr = N_("changes");
Bram Moolenaar1e607892006-03-13 22:15:53 +00003022 }
3023
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003024 if (curbuf->b_u_curhead != NULL)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00003025 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003026 // For ":undo N" we prefer a "after #N" message.
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003027 if (absolute && curbuf->b_u_curhead->uh_next.ptr != NULL)
Bram Moolenaardb552d602006-03-23 22:59:57 +00003028 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003029 uhp = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaardb552d602006-03-23 22:59:57 +00003030 did_undo = FALSE;
3031 }
3032 else if (did_undo)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00003033 uhp = curbuf->b_u_curhead;
3034 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003035 uhp = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00003036 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00003037 else
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003038 uhp = curbuf->b_u_newhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +00003039
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003040 if (uhp == NULL)
3041 *msgbuf = NUL;
3042 else
Bram Moolenaar52410572019-10-27 05:12:45 +01003043 add_time(msgbuf, sizeof(msgbuf), uhp->uh_time);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003044
Bram Moolenaarb2c03502010-07-02 20:20:09 +02003045#ifdef FEAT_CONCEAL
3046 {
3047 win_T *wp;
3048
3049 FOR_ALL_WINDOWS(wp)
3050 {
Bram Moolenaarf5963f72010-07-23 22:10:27 +02003051 if (wp->w_buffer == curbuf && wp->w_p_cole > 0)
Bram Moolenaara4d158b2022-08-14 14:17:45 +01003052 redraw_win_later(wp, UPD_NOT_VALID);
Bram Moolenaarb2c03502010-07-02 20:20:09 +02003053 }
3054 }
3055#endif
Bram Moolenaar8d02ce12022-01-25 18:24:00 +00003056 if (VIsual_active)
3057 check_pos(curbuf, &VIsual);
Bram Moolenaarb2c03502010-07-02 20:20:09 +02003058
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003059 smsg_attr_keep(0, _("%ld %s; %s #%ld %s"),
Bram Moolenaarca003e12006-03-17 23:19:38 +00003060 u_oldcount < 0 ? -u_oldcount : u_oldcount,
Bram Moolenaar89d40322006-08-29 15:30:07 +00003061 _(msgstr),
Bram Moolenaar433f7c82006-03-21 21:29:36 +00003062 did_undo ? _("before") : _("after"),
3063 uhp == NULL ? 0L : uhp->uh_seq,
3064 msgbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003065}
3066
3067/*
3068 * u_sync: stop adding to the current entry list
3069 */
3070 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003071u_sync(
Bram Moolenaare38eab22019-12-05 21:50:01 +01003072 int force) // Also sync when no_u_sync is set.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003073{
Bram Moolenaare38eab22019-12-05 21:50:01 +01003074 // Skip it when already synced or syncing is disabled.
Bram Moolenaar779b74b2006-04-10 14:55:34 +00003075 if (curbuf->b_u_synced || (!force && no_u_sync > 0))
3076 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003077#if defined(FEAT_XIM) && defined(FEAT_GUI_GTK)
Bram Moolenaar5c6dbcb2017-08-30 22:00:20 +02003078 if (p_imst == IM_ON_THE_SPOT && im_is_preediting())
Bram Moolenaare38eab22019-12-05 21:50:01 +01003079 return; // XIM is busy, don't break an undo sequence
Bram Moolenaar071d4272004-06-13 20:20:40 +00003080#endif
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +01003081 if (get_undolevel() < 0)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003082 curbuf->b_u_synced = TRUE; // no entries, nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00003083 else
3084 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003085 u_getbot(); // compute ue_bot of previous u_save
Bram Moolenaar071d4272004-06-13 20:20:40 +00003086 curbuf->b_u_curhead = NULL;
3087 }
3088}
3089
3090/*
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003091 * ":undolist": List the leafs of the undo tree
3092 */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003093 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003094ex_undolist(exarg_T *eap UNUSED)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003095{
3096 garray_T ga;
3097 u_header_T *uhp;
3098 int mark;
3099 int nomark;
3100 int changes = 1;
3101 int i;
3102
3103 /*
3104 * 1: walk the tree to find all leafs, put the info in "ga".
3105 * 2: sort the lines
3106 * 3: display the list
3107 */
3108 mark = ++lastmark;
3109 nomark = ++lastmark;
Bram Moolenaar04935fb2022-01-08 16:19:22 +00003110 ga_init2(&ga, sizeof(char *), 20);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003111
3112 uhp = curbuf->b_u_oldhead;
3113 while (uhp != NULL)
3114 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003115 if (uhp->uh_prev.ptr == NULL && uhp->uh_walk != nomark
Bram Moolenaarca003e12006-03-17 23:19:38 +00003116 && uhp->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003117 {
3118 if (ga_grow(&ga, 1) == FAIL)
3119 break;
Bram Moolenaarea391762018-04-08 13:07:22 +02003120 vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7d ",
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003121 uhp->uh_seq, changes);
Bram Moolenaar52410572019-10-27 05:12:45 +01003122 add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff),
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003123 uhp->uh_time);
Bram Moolenaara800b422010-06-27 01:15:55 +02003124 if (uhp->uh_save_nr > 0)
3125 {
Bram Moolenaardba01a02010-11-03 19:32:42 +01003126 while (STRLEN(IObuff) < 33)
Bram Moolenaara800b422010-06-27 01:15:55 +02003127 STRCAT(IObuff, " ");
3128 vim_snprintf_add((char *)IObuff, IOSIZE,
3129 " %3ld", uhp->uh_save_nr);
3130 }
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003131 ((char_u **)(ga.ga_data))[ga.ga_len++] = vim_strsave(IObuff);
3132 }
3133
3134 uhp->uh_walk = mark;
3135
Bram Moolenaare38eab22019-12-05 21:50:01 +01003136 // go down in the tree if we haven't been there
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003137 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
3138 && uhp->uh_prev.ptr->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003139 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003140 uhp = uhp->uh_prev.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003141 ++changes;
3142 }
3143
Bram Moolenaare38eab22019-12-05 21:50:01 +01003144 // go to alternate branch if we haven't been there
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003145 else if (uhp->uh_alt_next.ptr != NULL
3146 && uhp->uh_alt_next.ptr->uh_walk != nomark
3147 && uhp->uh_alt_next.ptr->uh_walk != mark)
3148 uhp = uhp->uh_alt_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003149
Bram Moolenaare38eab22019-12-05 21:50:01 +01003150 // go up in the tree if we haven't been there and we are at the
3151 // start of alternate branches
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003152 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
3153 && uhp->uh_next.ptr->uh_walk != nomark
3154 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003155 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003156 uhp = uhp->uh_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003157 --changes;
3158 }
3159
3160 else
3161 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003162 // need to backtrack; mark this node as done
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003163 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003164 if (uhp->uh_alt_prev.ptr != NULL)
3165 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003166 else
3167 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003168 uhp = uhp->uh_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003169 --changes;
3170 }
3171 }
3172 }
3173
3174 if (ga.ga_len == 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01003175 msg(_("Nothing to undo"));
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003176 else
3177 {
3178 sort_strings((char_u **)ga.ga_data, ga.ga_len);
3179
3180 msg_start();
Bram Moolenaar32526b32019-01-19 17:43:09 +01003181 msg_puts_attr(_("number changes when saved"),
Bram Moolenaar8820b482017-03-16 17:23:31 +01003182 HL_ATTR(HLF_T));
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003183 for (i = 0; i < ga.ga_len && !got_int; ++i)
3184 {
3185 msg_putchar('\n');
3186 if (got_int)
3187 break;
Bram Moolenaar32526b32019-01-19 17:43:09 +01003188 msg_puts(((char **)ga.ga_data)[i]);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003189 }
3190 msg_end();
3191
3192 ga_clear_strings(&ga);
3193 }
3194}
3195
3196/*
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003197 * ":undojoin": continue adding to the last entry list
3198 */
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003199 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003200ex_undojoin(exarg_T *eap UNUSED)
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003201{
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003202 if (curbuf->b_u_newhead == NULL)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003203 return; // nothing changed before
Bram Moolenaar57657d82006-04-21 22:12:41 +00003204 if (curbuf->b_u_curhead != NULL)
3205 {
Bram Moolenaar677658a2022-01-05 16:09:06 +00003206 emsg(_(e_undojoin_is_not_allowed_after_undo));
Bram Moolenaar57657d82006-04-21 22:12:41 +00003207 return;
3208 }
3209 if (!curbuf->b_u_synced)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003210 return; // already unsynced
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +01003211 if (get_undolevel() < 0)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003212 return; // no entries, nothing to do
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003213 else
Bram Moolenaare38eab22019-12-05 21:50:01 +01003214 // Append next change to the last entry
Bram Moolenaar5e4e1b12017-01-17 22:09:45 +01003215 curbuf->b_u_synced = FALSE;
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003216}
3217
3218/*
Bram Moolenaar59f931e2010-07-24 20:27:03 +02003219 * Called after writing or reloading the file and setting b_changed to FALSE.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003220 * Now an undo means that the buffer is modified.
3221 */
3222 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003223u_unchanged(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003224{
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003225 u_unch_branch(buf->b_u_oldhead);
3226 buf->b_did_warn = FALSE;
3227}
3228
Bram Moolenaar730cde92010-06-27 05:18:54 +02003229/*
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003230 * After reloading a buffer which was saved for 'undoreload': Find the first
3231 * line that was changed and set the cursor there.
3232 */
3233 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003234u_find_first_changed(void)
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003235{
3236 u_header_T *uhp = curbuf->b_u_newhead;
3237 u_entry_T *uep;
3238 linenr_T lnum;
3239
3240 if (curbuf->b_u_curhead != NULL || uhp == NULL)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003241 return; // undid something in an autocmd?
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003242
Bram Moolenaare38eab22019-12-05 21:50:01 +01003243 // Check that the last undo block was for the whole file.
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003244 uep = uhp->uh_entry;
3245 if (uep->ue_top != 0 || uep->ue_bot != 0)
3246 return;
3247
3248 for (lnum = 1; lnum < curbuf->b_ml.ml_line_count
3249 && lnum <= uep->ue_size; ++lnum)
Bram Moolenaarccae4672019-01-04 15:09:57 +01003250 {
3251 char_u *p = ml_get_buf(curbuf, lnum, FALSE);
3252
3253 if (uep->ue_array[lnum - 1].ul_len != curbuf->b_ml.ml_line_len
3254 || memcmp(p, uep->ue_array[lnum - 1].ul_line, uep->ue_array[lnum - 1].ul_len) != 0)
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003255 {
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01003256 CLEAR_POS(&(uhp->uh_cursor));
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003257 uhp->uh_cursor.lnum = lnum;
3258 return;
3259 }
Bram Moolenaarccae4672019-01-04 15:09:57 +01003260 }
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003261 if (curbuf->b_ml.ml_line_count != uep->ue_size)
3262 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003263 // lines added or deleted at the end, put the cursor there
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01003264 CLEAR_POS(&(uhp->uh_cursor));
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003265 uhp->uh_cursor.lnum = lnum;
3266 }
3267}
3268
3269/*
Bram Moolenaar730cde92010-06-27 05:18:54 +02003270 * Increase the write count, store it in the last undo header, what would be
3271 * used for "u".
3272 */
3273 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003274u_update_save_nr(buf_T *buf)
Bram Moolenaar730cde92010-06-27 05:18:54 +02003275{
3276 u_header_T *uhp;
3277
3278 ++buf->b_u_save_nr_last;
3279 buf->b_u_save_nr_cur = buf->b_u_save_nr_last;
3280 uhp = buf->b_u_curhead;
3281 if (uhp != NULL)
3282 uhp = uhp->uh_next.ptr;
3283 else
3284 uhp = buf->b_u_newhead;
3285 if (uhp != NULL)
3286 uhp->uh_save_nr = buf->b_u_save_nr_last;
3287}
3288
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003289 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003290u_unch_branch(u_header_T *uhp)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003291{
Bram Moolenaar1e607892006-03-13 22:15:53 +00003292 u_header_T *uh;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003293
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003294 for (uh = uhp; uh != NULL; uh = uh->uh_prev.ptr)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003295 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003296 uh->uh_flags |= UH_CHANGED;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003297 if (uh->uh_alt_next.ptr != NULL)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003298 u_unch_branch(uh->uh_alt_next.ptr); // recursive
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003299 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003300}
3301
3302/*
3303 * Get pointer to last added entry.
3304 * If it's not valid, give an error message and return NULL.
3305 */
3306 static u_entry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003307u_get_headentry(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003308{
3309 if (curbuf->b_u_newhead == NULL || curbuf->b_u_newhead->uh_entry == NULL)
3310 {
RestorerZ68ebcee2023-05-31 17:12:14 +01003311 iemsg(e_undo_list_corrupt);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003312 return NULL;
3313 }
3314 return curbuf->b_u_newhead->uh_entry;
3315}
3316
3317/*
3318 * u_getbot(): compute the line number of the previous u_save
3319 * It is called only when b_u_synced is FALSE.
3320 */
3321 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003322u_getbot(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003323{
3324 u_entry_T *uep;
3325 linenr_T extra;
3326
Bram Moolenaare38eab22019-12-05 21:50:01 +01003327 uep = u_get_headentry(); // check for corrupt undo list
Bram Moolenaar071d4272004-06-13 20:20:40 +00003328 if (uep == NULL)
3329 return;
3330
3331 uep = curbuf->b_u_newhead->uh_getbot_entry;
3332 if (uep != NULL)
3333 {
3334 /*
3335 * the new ue_bot is computed from the number of lines that has been
3336 * inserted (0 - deleted) since calling u_save. This is equal to the
3337 * old line count subtracted from the current line count.
3338 */
3339 extra = curbuf->b_ml.ml_line_count - uep->ue_lcount;
3340 uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra;
3341 if (uep->ue_bot < 1 || uep->ue_bot > curbuf->b_ml.ml_line_count)
3342 {
RestorerZ68ebcee2023-05-31 17:12:14 +01003343 iemsg(e_undo_line_missing);
Bram Moolenaare38eab22019-12-05 21:50:01 +01003344 uep->ue_bot = uep->ue_top + 1; // assume all lines deleted, will
3345 // get all the old lines back
3346 // without deleting the current
3347 // ones
Bram Moolenaar071d4272004-06-13 20:20:40 +00003348 }
3349
3350 curbuf->b_u_newhead->uh_getbot_entry = NULL;
3351 }
3352
3353 curbuf->b_u_synced = TRUE;
3354}
3355
3356/*
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003357 * Free one header "uhp" and its entry list and adjust the pointers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003358 */
3359 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003360u_freeheader(
3361 buf_T *buf,
3362 u_header_T *uhp,
Bram Moolenaare38eab22019-12-05 21:50:01 +01003363 u_header_T **uhpp) // if not NULL reset when freeing this header
Bram Moolenaar071d4272004-06-13 20:20:40 +00003364{
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003365 u_header_T *uhap;
3366
Bram Moolenaare38eab22019-12-05 21:50:01 +01003367 // When there is an alternate redo list free that branch completely,
3368 // because we can never go there.
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003369 if (uhp->uh_alt_next.ptr != NULL)
3370 u_freebranch(buf, uhp->uh_alt_next.ptr, uhpp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003371
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003372 if (uhp->uh_alt_prev.ptr != NULL)
3373 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003374
Bram Moolenaare38eab22019-12-05 21:50:01 +01003375 // Update the links in the list to remove the header.
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003376 if (uhp->uh_next.ptr == NULL)
3377 buf->b_u_oldhead = uhp->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003378 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003379 uhp->uh_next.ptr->uh_prev.ptr = uhp->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003380
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003381 if (uhp->uh_prev.ptr == NULL)
3382 buf->b_u_newhead = uhp->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003383 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003384 for (uhap = uhp->uh_prev.ptr; uhap != NULL;
3385 uhap = uhap->uh_alt_next.ptr)
3386 uhap->uh_next.ptr = uhp->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003387
Bram Moolenaar1e607892006-03-13 22:15:53 +00003388 u_freeentries(buf, uhp, uhpp);
3389}
3390
3391/*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003392 * Free an alternate branch and any following alternate branches.
Bram Moolenaar1e607892006-03-13 22:15:53 +00003393 */
3394 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003395u_freebranch(
3396 buf_T *buf,
3397 u_header_T *uhp,
Bram Moolenaare38eab22019-12-05 21:50:01 +01003398 u_header_T **uhpp) // if not NULL reset when freeing this header
Bram Moolenaar1e607892006-03-13 22:15:53 +00003399{
3400 u_header_T *tofree, *next;
3401
Bram Moolenaare38eab22019-12-05 21:50:01 +01003402 // If this is the top branch we may need to use u_freeheader() to update
3403 // all the pointers.
Bram Moolenaar07d06772007-11-10 21:51:15 +00003404 if (uhp == buf->b_u_oldhead)
3405 {
Bram Moolenaaraa887322013-11-07 03:04:11 +01003406 while (buf->b_u_oldhead != NULL)
3407 u_freeheader(buf, buf->b_u_oldhead, uhpp);
Bram Moolenaar07d06772007-11-10 21:51:15 +00003408 return;
3409 }
3410
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003411 if (uhp->uh_alt_prev.ptr != NULL)
3412 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +00003413
3414 next = uhp;
3415 while (next != NULL)
3416 {
3417 tofree = next;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003418 if (tofree->uh_alt_next.ptr != NULL)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003419 u_freebranch(buf, tofree->uh_alt_next.ptr, uhpp); // recursive
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003420 next = tofree->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00003421 u_freeentries(buf, tofree, uhpp);
3422 }
3423}
3424
3425/*
3426 * Free all the undo entries for one header and the header itself.
3427 * This means that "uhp" is invalid when returning.
3428 */
3429 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003430u_freeentries(
3431 buf_T *buf,
3432 u_header_T *uhp,
Bram Moolenaare38eab22019-12-05 21:50:01 +01003433 u_header_T **uhpp) // if not NULL reset when freeing this header
Bram Moolenaar1e607892006-03-13 22:15:53 +00003434{
3435 u_entry_T *uep, *nuep;
3436
Bram Moolenaare38eab22019-12-05 21:50:01 +01003437 // Check for pointers to the header that become invalid now.
Bram Moolenaar1e607892006-03-13 22:15:53 +00003438 if (buf->b_u_curhead == uhp)
3439 buf->b_u_curhead = NULL;
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003440 if (buf->b_u_newhead == uhp)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003441 buf->b_u_newhead = NULL; // freeing the newest entry
Bram Moolenaar1e607892006-03-13 22:15:53 +00003442 if (uhpp != NULL && uhp == *uhpp)
3443 *uhpp = NULL;
3444
3445 for (uep = uhp->uh_entry; uep != NULL; uep = nuep)
3446 {
3447 nuep = uep->ue_next;
3448 u_freeentry(uep, uep->ue_size);
3449 }
3450
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003451#ifdef U_DEBUG
3452 uhp->uh_magic = 0;
3453#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003454 vim_free((char_u *)uhp);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00003455 --buf->b_u_numhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003456}
3457
3458/*
3459 * free entry 'uep' and 'n' lines in uep->ue_array[]
3460 */
3461 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003462u_freeentry(u_entry_T *uep, long n)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003463{
Bram Moolenaar8d343302005-07-12 22:46:17 +00003464 while (n > 0)
Bram Moolenaarccae4672019-01-04 15:09:57 +01003465 vim_free(uep->ue_array[--n].ul_line);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003466 vim_free((char_u *)uep->ue_array);
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003467#ifdef U_DEBUG
3468 uep->ue_magic = 0;
3469#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003470 vim_free((char_u *)uep);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003471}
3472
3473/*
3474 * invalidate the undo buffer; called when storage has already been released
3475 */
Christian Brabandt9071ed82024-02-15 20:17:37 +01003476 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003477u_clearall(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003478{
3479 buf->b_u_newhead = buf->b_u_oldhead = buf->b_u_curhead = NULL;
3480 buf->b_u_synced = TRUE;
3481 buf->b_u_numhead = 0;
Bram Moolenaarccae4672019-01-04 15:09:57 +01003482 buf->b_u_line_ptr.ul_line = NULL;
3483 buf->b_u_line_ptr.ul_len = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003484 buf->b_u_line_lnum = 0;
3485}
3486
3487/*
Christian Brabandt9071ed82024-02-15 20:17:37 +01003488 * Free all allocated memory blocks for the buffer 'buf'.
3489 */
3490 static void
3491u_blockfree(buf_T *buf)
3492{
3493 while (buf->b_u_oldhead != NULL)
3494 u_freeheader(buf, buf->b_u_oldhead, NULL);
3495 vim_free(buf->b_u_line_ptr.ul_line);
3496}
3497
3498/*
3499 * Free all allocated memory blocks for the buffer 'buf'.
3500 * and invalidate the undo buffer
3501 */
3502 void
3503u_clearallandblockfree(buf_T *buf)
3504{
3505 u_blockfree(buf);
3506 u_clearall(buf);
3507}
3508
3509
3510
3511/*
Bram Moolenaarccae4672019-01-04 15:09:57 +01003512 * Save the line "lnum" for the "U" command.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003513 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02003514 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003515u_saveline(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003516{
Bram Moolenaare38eab22019-12-05 21:50:01 +01003517 if (lnum == curbuf->b_u_line_lnum) // line is already saved
Bram Moolenaar071d4272004-06-13 20:20:40 +00003518 return;
Bram Moolenaare38eab22019-12-05 21:50:01 +01003519 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) // should never happen
Bram Moolenaar071d4272004-06-13 20:20:40 +00003520 return;
3521 u_clearline();
3522 curbuf->b_u_line_lnum = lnum;
3523 if (curwin->w_cursor.lnum == lnum)
3524 curbuf->b_u_line_colnr = curwin->w_cursor.col;
3525 else
3526 curbuf->b_u_line_colnr = 0;
Bram Moolenaarccae4672019-01-04 15:09:57 +01003527 if (u_save_line(&curbuf->b_u_line_ptr, lnum) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003528 do_outofmem_msg((long_u)0);
3529}
3530
3531/*
3532 * clear the line saved for the "U" command
3533 * (this is used externally for crossing a line while in insert mode)
3534 */
3535 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003536u_clearline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003537{
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00003538 if (curbuf->b_u_line_ptr.ul_line == NULL)
3539 return;
3540
3541 VIM_CLEAR(curbuf->b_u_line_ptr.ul_line);
3542 curbuf->b_u_line_ptr.ul_len = 0;
3543 curbuf->b_u_line_lnum = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003544}
3545
3546/*
3547 * Implementation of the "U" command.
3548 * Differentiation from vi: "U" can be undone with the next "U".
3549 * We also allow the cursor to be in another line.
Bram Moolenaard04b7502010-07-08 22:27:55 +02003550 * Careful: may trigger autocommands that reload the buffer.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003551 */
3552 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003553u_undoline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003554{
Bram Moolenaarccae4672019-01-04 15:09:57 +01003555 colnr_T t;
3556 undoline_T oldp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003557
3558 if (undo_off)
3559 return;
3560
Bram Moolenaarccae4672019-01-04 15:09:57 +01003561 if (curbuf->b_u_line_ptr.ul_line == NULL
Bram Moolenaare3300c82008-02-13 14:21:38 +00003562 || curbuf->b_u_line_lnum > curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003563 {
3564 beep_flush();
3565 return;
3566 }
Bram Moolenaare3300c82008-02-13 14:21:38 +00003567
Bram Moolenaarccae4672019-01-04 15:09:57 +01003568 // first save the line for the 'u' command
Bram Moolenaar071d4272004-06-13 20:20:40 +00003569 if (u_savecommon(curbuf->b_u_line_lnum - 1,
Bram Moolenaar59f931e2010-07-24 20:27:03 +02003570 curbuf->b_u_line_lnum + 1, (linenr_T)0, FALSE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003571 return;
Bram Moolenaarccae4672019-01-04 15:09:57 +01003572 if (u_save_line(&oldp, curbuf->b_u_line_lnum) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003573 {
3574 do_outofmem_msg((long_u)0);
3575 return;
3576 }
Bram Moolenaar02c037a2020-08-30 19:26:45 +02003577 ml_replace_len(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr.ul_line,
3578 curbuf->b_u_line_ptr.ul_len, TRUE, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003579 changed_bytes(curbuf->b_u_line_lnum, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003580 curbuf->b_u_line_ptr = oldp;
3581
3582 t = curbuf->b_u_line_colnr;
3583 if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum)
3584 curbuf->b_u_line_colnr = curwin->w_cursor.col;
3585 curwin->w_cursor.col = t;
3586 curwin->w_cursor.lnum = curbuf->b_u_line_lnum;
Bram Moolenaare3300c82008-02-13 14:21:38 +00003587 check_cursor_col();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003588}
3589
3590/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003591 * Check if the 'modified' flag is set, or 'ff' has changed (only need to
3592 * check the first character, because it can only be "dos", "unix" or "mac").
3593 * "nofile" and "scratch" type buffers are considered to always be unchanged.
Bram Moolenaarf405c8f2017-12-09 19:51:49 +01003594 * Also considers a buffer changed when a terminal window contains a running
3595 * job.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003596 */
3597 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003598bufIsChanged(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003599{
Bram Moolenaareb44a682017-08-03 22:44:55 +02003600#ifdef FEAT_TERMINAL
Bram Moolenaar9e636b92022-05-29 22:37:05 +01003601 if (term_job_running_not_none(buf->b_term))
Bram Moolenaareb44a682017-08-03 22:44:55 +02003602 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003603#endif
Bram Moolenaarf405c8f2017-12-09 19:51:49 +01003604 return bufIsChangedNotTerm(buf);
3605}
3606
3607/*
Bram Moolenaara84a3dd2019-03-25 22:21:24 +01003608 * Return TRUE if any buffer has changes. Also buffers that are not written.
3609 */
3610 int
3611anyBufIsChanged(void)
3612{
3613 buf_T *buf;
3614
3615 FOR_ALL_BUFFERS(buf)
3616 if (bufIsChanged(buf))
3617 return TRUE;
Bram Moolenaard6c3f1f2019-03-26 00:31:21 +01003618 return FALSE;
Bram Moolenaara84a3dd2019-03-25 22:21:24 +01003619}
3620
3621/*
Bram Moolenaarf405c8f2017-12-09 19:51:49 +01003622 * Like bufIsChanged() but ignoring a terminal window.
3623 */
3624 int
3625bufIsChangedNotTerm(buf_T *buf)
3626{
Bram Moolenaar4551c0a2018-06-20 22:38:21 +02003627 // In a "prompt" buffer we do respect 'modified', so that we can control
3628 // closing the window by setting or resetting that option.
3629 return (!bt_dontwrite(buf) || bt_prompt(buf))
Bram Moolenaareb44a682017-08-03 22:44:55 +02003630 && (buf->b_changed || file_ff_differs(buf, TRUE));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003631}
3632
3633 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003634curbufIsChanged(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003635{
Bram Moolenaareb44a682017-08-03 22:44:55 +02003636 return bufIsChanged(curbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003637}
Bram Moolenaara800b422010-06-27 01:15:55 +02003638
3639#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003640
Bram Moolenaara800b422010-06-27 01:15:55 +02003641/*
3642 * For undotree(): Append the list of undo blocks at "first_uhp" to "list".
3643 * Recursive.
3644 */
Bram Moolenaar840d16f2019-09-10 21:27:18 +02003645 static void
Devin J. Pohly5fee1112023-04-23 20:26:59 -05003646u_eval_tree(buf_T *buf, u_header_T *first_uhp, list_T *list)
Bram Moolenaara800b422010-06-27 01:15:55 +02003647{
3648 u_header_T *uhp = first_uhp;
3649 dict_T *dict;
3650
3651 while (uhp != NULL)
3652 {
3653 dict = dict_alloc();
3654 if (dict == NULL)
3655 return;
Bram Moolenaare0be1672018-07-08 16:50:37 +02003656 dict_add_number(dict, "seq", uhp->uh_seq);
3657 dict_add_number(dict, "time", (long)uhp->uh_time);
Devin J. Pohly5fee1112023-04-23 20:26:59 -05003658 if (uhp == buf->b_u_newhead)
Bram Moolenaare0be1672018-07-08 16:50:37 +02003659 dict_add_number(dict, "newhead", 1);
Devin J. Pohly5fee1112023-04-23 20:26:59 -05003660 if (uhp == buf->b_u_curhead)
Bram Moolenaare0be1672018-07-08 16:50:37 +02003661 dict_add_number(dict, "curhead", 1);
Bram Moolenaara800b422010-06-27 01:15:55 +02003662 if (uhp->uh_save_nr > 0)
Bram Moolenaare0be1672018-07-08 16:50:37 +02003663 dict_add_number(dict, "save", uhp->uh_save_nr);
Bram Moolenaara800b422010-06-27 01:15:55 +02003664
3665 if (uhp->uh_alt_next.ptr != NULL)
3666 {
3667 list_T *alt_list = list_alloc();
3668
3669 if (alt_list != NULL)
3670 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003671 // Recursive call to add alternate undo tree.
Devin J. Pohly5fee1112023-04-23 20:26:59 -05003672 u_eval_tree(buf, uhp->uh_alt_next.ptr, alt_list);
Bram Moolenaara800b422010-06-27 01:15:55 +02003673 dict_add_list(dict, "alt", alt_list);
3674 }
3675 }
3676
3677 list_append_dict(list, dict);
3678 uhp = uhp->uh_prev.ptr;
3679 }
3680}
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003681
3682/*
3683 * "undofile(name)" function
3684 */
3685 void
3686f_undofile(typval_T *argvars UNUSED, typval_T *rettv)
3687{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02003688 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
3689 return;
3690
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003691 rettv->v_type = VAR_STRING;
3692#ifdef FEAT_PERSISTENT_UNDO
3693 {
3694 char_u *fname = tv_get_string(&argvars[0]);
3695
3696 if (*fname == NUL)
3697 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003698 // If there is no file name there will be no undo file.
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003699 rettv->vval.v_string = NULL;
3700 }
3701 else
3702 {
3703 char_u *ffname = FullName_save(fname, TRUE);
3704
3705 if (ffname != NULL)
3706 rettv->vval.v_string = u_get_undo_file_name(ffname, FALSE);
3707 vim_free(ffname);
3708 }
3709 }
3710#else
3711 rettv->vval.v_string = NULL;
3712#endif
3713}
Christian Brabandt8a4c8122021-07-25 14:36:05 +02003714#ifdef FEAT_PERSISTENT_UNDO
3715/*
3716 * Reset undofile option and delete the undofile
3717 */
3718 void
3719u_undofile_reset_and_delete(buf_T *buf)
3720{
3721 char_u *file_name;
3722
3723 if (!buf->b_p_udf)
3724 return;
3725
3726 file_name = u_get_undo_file_name(buf->b_ffname, TRUE);
3727 if (file_name != NULL)
3728 {
3729 mch_remove(file_name);
3730 vim_free(file_name);
3731 }
3732
Bram Moolenaar31e5c602022-04-15 13:53:33 +01003733 set_option_value_give_err((char_u *)"undofile", 0L, NULL, OPT_LOCAL);
Christian Brabandt8a4c8122021-07-25 14:36:05 +02003734}
3735 #endif
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003736
3737/*
Devin J. Pohly5fee1112023-04-23 20:26:59 -05003738 * "undotree(expr)" function
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003739 */
3740 void
3741f_undotree(typval_T *argvars UNUSED, typval_T *rettv)
3742{
Devin J. Pohly5fee1112023-04-23 20:26:59 -05003743 if (in_vim9script() && check_for_opt_buffer_arg(argvars, 0) == FAIL)
3744 return;
3745
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00003746 if (rettv_dict_alloc(rettv) == FAIL)
3747 return;
3748
zeertzjqab9f2ec2023-08-20 18:35:10 +02003749 typval_T *tv = &argvars[0];
3750 buf_T *buf = tv->v_type == VAR_UNKNOWN ? curbuf : get_buf_arg(tv);
3751 if (buf == NULL)
3752 return;
3753
3754 dict_T *dict = rettv->vval.v_dict;
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00003755
Devin J. Pohly5fee1112023-04-23 20:26:59 -05003756 dict_add_number(dict, "synced", (long)buf->b_u_synced);
3757 dict_add_number(dict, "seq_last", buf->b_u_seq_last);
3758 dict_add_number(dict, "save_last", buf->b_u_save_nr_last);
3759 dict_add_number(dict, "seq_cur", buf->b_u_seq_cur);
3760 dict_add_number(dict, "time_cur", (long)buf->b_u_time_cur);
3761 dict_add_number(dict, "save_cur", buf->b_u_save_nr_cur);
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00003762
zeertzjqab9f2ec2023-08-20 18:35:10 +02003763 list_T *list = list_alloc();
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00003764 if (list != NULL)
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003765 {
Devin J. Pohly5fee1112023-04-23 20:26:59 -05003766 u_eval_tree(buf, buf->b_u_oldhead, list);
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00003767 dict_add_list(dict, "entries", list);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003768 }
3769}
3770
Bram Moolenaara800b422010-06-27 01:15:55 +02003771#endif