blob: 764912ae61c3d2ebd1982dc4d24c321a705b0add [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
John Marriottfd1a8382024-11-06 21:21:50 +0100364 ul->ul_textlen = ml_get_len(lnum);
Bram Moolenaarccae4672019-01-04 15:09:57 +0100365 if (curbuf->b_ml.ml_line_len == 0)
366 {
367 ul->ul_len = 1;
368 ul->ul_line = vim_strsave((char_u *)"");
369 }
370 else
371 {
Bram Moolenaar964b3742019-05-24 18:54:09 +0200372 // This uses the length in the memline, thus text properties are
373 // included.
Bram Moolenaarccae4672019-01-04 15:09:57 +0100374 ul->ul_len = curbuf->b_ml.ml_line_len;
375 ul->ul_line = vim_memsave(line, ul->ul_len);
376 }
377 return ul->ul_line == NULL ? FAIL : OK;
378}
379
Bram Moolenaard5c2c772020-05-30 16:17:33 +0200380#ifdef FEAT_PROP_POPUP
Bram Moolenaarccae4672019-01-04 15:09:57 +0100381/*
Bram Moolenaara9d4b842020-05-30 14:46:52 +0200382 * return TRUE if line "lnum" has text property "flags".
383 */
384 static int
385has_prop_w_flags(linenr_T lnum, int flags)
386{
387 char_u *props;
388 int i;
389 int proplen = get_text_props(curbuf, lnum, &props, FALSE);
390
391 for (i = 0; i < proplen; ++i)
392 {
393 textprop_T prop;
394
395 mch_memmove(&prop, props + i * sizeof prop, sizeof prop);
396 if (prop.tp_flags & flags)
397 return TRUE;
398 }
399 return FALSE;
400}
Bram Moolenaard5c2c772020-05-30 16:17:33 +0200401#endif
Bram Moolenaara9d4b842020-05-30 14:46:52 +0200402
403/*
Bram Moolenaarb0b50882010-07-07 18:26:28 +0200404 * Common code for various ways to save text before a change.
Bram Moolenaard04b7502010-07-08 22:27:55 +0200405 * "top" is the line above the first changed line.
406 * "bot" is the line below the last changed line.
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200407 * "newbot" is the new bottom line. Use zero when not known.
408 * "reload" is TRUE when saving for a buffer reload.
Bram Moolenaard04b7502010-07-08 22:27:55 +0200409 * Careful: may trigger autocommands that reload the buffer.
410 * Returns FAIL when lines could not be saved, OK otherwise.
Bram Moolenaarb0b50882010-07-07 18:26:28 +0200411 */
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200412 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100413u_savecommon(
414 linenr_T top,
415 linenr_T bot,
416 linenr_T newbot,
417 int reload)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000418{
Bram Moolenaar1e607892006-03-13 22:15:53 +0000419 linenr_T lnum;
420 long i;
421 u_header_T *uhp;
422 u_header_T *old_curhead;
423 u_entry_T *uep;
424 u_entry_T *prev_uep;
425 long size;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000426
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200427 if (!reload)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000428 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100429 // When making changes is not allowed return FAIL. It's a crude way
430 // to make all change commands fail.
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200431 if (!undo_allowed())
Bram Moolenaar009b2592004-10-24 19:18:58 +0000432 return FAIL;
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200433
434#ifdef FEAT_NETBEANS_INTG
435 /*
436 * Netbeans defines areas that cannot be modified. Bail out here when
437 * trying to change text in a guarded area.
438 */
439 if (netbeans_active())
Bram Moolenaar009b2592004-10-24 19:18:58 +0000440 {
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200441 if (netbeans_is_guarded(top, bot))
442 {
Bram Moolenaar74409f62022-01-01 15:58:22 +0000443 emsg(_(e_region_is_guarded_cannot_modify));
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200444 return FAIL;
445 }
446 if (curbuf->b_p_ro)
447 {
Bram Moolenaar74409f62022-01-01 15:58:22 +0000448 emsg(_(e_netbeans_does_not_allow_changes_in_read_only_files));
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200449 return FAIL;
450 }
Bram Moolenaar009b2592004-10-24 19:18:58 +0000451 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000452#endif
Bram Moolenaar63ecdda2017-07-28 22:29:35 +0200453#ifdef FEAT_TERMINAL
Bram Moolenaare38eab22019-12-05 21:50:01 +0100454 // A change in a terminal buffer removes the highlighting.
Bram Moolenaar63ecdda2017-07-28 22:29:35 +0200455 term_change_in_curbuf();
456#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000457
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200458 /*
459 * Saving text for undo means we are going to make a change. Give a
460 * warning for a read-only file before making the change, so that the
461 * FileChangedRO event can replace the buffer with a read-write version
462 * (e.g., obtained from a source control system).
463 */
464 change_warning(0);
465 if (bot > curbuf->b_ml.ml_line_count + 1)
466 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100467 // This happens when the FileChangedRO autocommand changes the
468 // file in a way it becomes shorter.
Bram Moolenaard82a47d2022-01-05 20:24:39 +0000469 emsg(_(e_line_count_changed_unexpectedly));
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200470 return FAIL;
471 }
Bram Moolenaard04b7502010-07-08 22:27:55 +0200472 }
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200473
474#ifdef U_DEBUG
475 u_check(FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000476#endif
477
Bram Moolenaara9d4b842020-05-30 14:46:52 +0200478#ifdef FEAT_PROP_POPUP
479 // Include the line above if a text property continues from it.
480 // Include the line below if a text property continues to it.
481 if (bot - top > 1)
482 {
483 if (top > 0 && has_prop_w_flags(top + 1, TP_FLAG_CONT_PREV))
484 --top;
485 if (bot <= curbuf->b_ml.ml_line_count
486 && has_prop_w_flags(bot - 1, TP_FLAG_CONT_NEXT))
487 {
488 ++bot;
489 if (newbot != 0)
490 ++newbot;
491 }
492 }
493#endif
494
Bram Moolenaar071d4272004-06-13 20:20:40 +0000495 size = bot - top - 1;
496
497 /*
Bram Moolenaarb0b50882010-07-07 18:26:28 +0200498 * If curbuf->b_u_synced == TRUE make a new header.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000499 */
500 if (curbuf->b_u_synced)
501 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100502 // Need to create new entry in b_changelist.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000503 curbuf->b_new_change = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000504
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +0100505 if (get_undolevel() >= 0)
Bram Moolenaar1e607892006-03-13 22:15:53 +0000506 {
507 /*
508 * Make a new header entry. Do this first so that we don't mess
509 * up the undo info when out of memory.
510 */
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200511 uhp = U_ALLOC_LINE(sizeof(u_header_T));
Bram Moolenaar1e607892006-03-13 22:15:53 +0000512 if (uhp == NULL)
513 goto nomem;
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000514#ifdef U_DEBUG
515 uhp->uh_magic = UH_MAGIC;
516#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +0000517 }
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000518 else
519 uhp = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000520
Bram Moolenaar071d4272004-06-13 20:20:40 +0000521 /*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000522 * If we undid more than we redid, move the entry lists before and
523 * including curbuf->b_u_curhead to an alternate branch.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000524 */
Bram Moolenaar1e607892006-03-13 22:15:53 +0000525 old_curhead = curbuf->b_u_curhead;
526 if (old_curhead != NULL)
527 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200528 curbuf->b_u_newhead = old_curhead->uh_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000529 curbuf->b_u_curhead = NULL;
530 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000531
532 /*
533 * free headers to keep the size right
534 */
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +0100535 while (curbuf->b_u_numhead > get_undolevel()
536 && curbuf->b_u_oldhead != NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +0000537 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000538 u_header_T *uhfree = curbuf->b_u_oldhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000539
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000540 if (uhfree == old_curhead)
Bram Moolenaare38eab22019-12-05 21:50:01 +0100541 // Can't reconnect the branch, delete all of it.
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000542 u_freebranch(curbuf, uhfree, &old_curhead);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200543 else if (uhfree->uh_alt_next.ptr == NULL)
Bram Moolenaare38eab22019-12-05 21:50:01 +0100544 // There is no branch, only free one header.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000545 u_freeheader(curbuf, uhfree, &old_curhead);
Bram Moolenaar1e607892006-03-13 22:15:53 +0000546 else
547 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100548 // Free the oldest alternate branch as a whole.
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200549 while (uhfree->uh_alt_next.ptr != NULL)
550 uhfree = uhfree->uh_alt_next.ptr;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000551 u_freebranch(curbuf, uhfree, &old_curhead);
Bram Moolenaar1e607892006-03-13 22:15:53 +0000552 }
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000553#ifdef U_DEBUG
554 u_check(TRUE);
555#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +0000556 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000557
Bram Moolenaare38eab22019-12-05 21:50:01 +0100558 if (uhp == NULL) // no undo at all
Bram Moolenaar071d4272004-06-13 20:20:40 +0000559 {
Bram Moolenaar1e607892006-03-13 22:15:53 +0000560 if (old_curhead != NULL)
561 u_freebranch(curbuf, old_curhead, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000562 curbuf->b_u_synced = FALSE;
563 return OK;
564 }
565
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200566 uhp->uh_prev.ptr = NULL;
567 uhp->uh_next.ptr = curbuf->b_u_newhead;
568 uhp->uh_alt_next.ptr = old_curhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000569 if (old_curhead != NULL)
570 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200571 uhp->uh_alt_prev.ptr = old_curhead->uh_alt_prev.ptr;
572 if (uhp->uh_alt_prev.ptr != NULL)
573 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = uhp;
574 old_curhead->uh_alt_prev.ptr = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000575 if (curbuf->b_u_oldhead == old_curhead)
576 curbuf->b_u_oldhead = uhp;
577 }
Bram Moolenaar89ed3df2007-01-09 19:23:12 +0000578 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200579 uhp->uh_alt_prev.ptr = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000580 if (curbuf->b_u_newhead != NULL)
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200581 curbuf->b_u_newhead->uh_prev.ptr = uhp;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000582
Bram Moolenaarca003e12006-03-17 23:19:38 +0000583 uhp->uh_seq = ++curbuf->b_u_seq_last;
584 curbuf->b_u_seq_cur = uhp->uh_seq;
Bram Moolenaar170b10b2016-07-29 16:15:27 +0200585 uhp->uh_time = vim_time();
Bram Moolenaara800b422010-06-27 01:15:55 +0200586 uhp->uh_save_nr = 0;
587 curbuf->b_u_time_cur = uhp->uh_time + 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000588
Bram Moolenaar1e607892006-03-13 22:15:53 +0000589 uhp->uh_walk = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000590 uhp->uh_entry = NULL;
591 uhp->uh_getbot_entry = NULL;
Bram Moolenaare38eab22019-12-05 21:50:01 +0100592 uhp->uh_cursor = curwin->w_cursor; // save cursor pos. for undo
Bram Moolenaar071d4272004-06-13 20:20:40 +0000593 if (virtual_active() && curwin->w_cursor.coladd > 0)
594 uhp->uh_cursor_vcol = getviscol();
595 else
596 uhp->uh_cursor_vcol = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000597
Bram Moolenaare38eab22019-12-05 21:50:01 +0100598 // save changed and buffer empty flag for undo
Bram Moolenaar071d4272004-06-13 20:20:40 +0000599 uhp->uh_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
600 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
601
Bram Moolenaare38eab22019-12-05 21:50:01 +0100602 // save named marks and Visual marks for undo
Bram Moolenaar071d4272004-06-13 20:20:40 +0000603 mch_memmove(uhp->uh_namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000604 uhp->uh_visual = curbuf->b_visual;
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000605
Bram Moolenaar071d4272004-06-13 20:20:40 +0000606 curbuf->b_u_newhead = uhp;
607 if (curbuf->b_u_oldhead == NULL)
608 curbuf->b_u_oldhead = uhp;
609 ++curbuf->b_u_numhead;
610 }
611 else
612 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100613 if (get_undolevel() < 0) // no undo at all
Bram Moolenaar071d4272004-06-13 20:20:40 +0000614 return OK;
615
616 /*
617 * When saving a single line, and it has been saved just before, it
618 * doesn't make sense saving it again. Saves a lot of memory when
619 * making lots of changes inside the same line.
620 * This is only possible if the previous change didn't increase or
621 * decrease the number of lines.
622 * Check the ten last changes. More doesn't make sense and takes too
623 * long.
624 */
625 if (size == 1)
626 {
627 uep = u_get_headentry();
628 prev_uep = NULL;
629 for (i = 0; i < 10; ++i)
630 {
631 if (uep == NULL)
632 break;
633
Bram Moolenaare38eab22019-12-05 21:50:01 +0100634 // If lines have been inserted/deleted we give up.
635 // Also when the line was included in a multi-line save.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000636 if ((curbuf->b_u_newhead->uh_getbot_entry != uep
637 ? (uep->ue_top + uep->ue_size + 1
638 != (uep->ue_bot == 0
639 ? curbuf->b_ml.ml_line_count + 1
640 : uep->ue_bot))
641 : uep->ue_lcount != curbuf->b_ml.ml_line_count)
642 || (uep->ue_size > 1
643 && top >= uep->ue_top
644 && top + 2 <= uep->ue_top + uep->ue_size + 1))
645 break;
646
Bram Moolenaare38eab22019-12-05 21:50:01 +0100647 // If it's the same line we can skip saving it again.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000648 if (uep->ue_size == 1 && uep->ue_top == top)
649 {
650 if (i > 0)
651 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100652 // It's not the last entry: get ue_bot for the last
653 // entry now. Following deleted/inserted lines go to
654 // the re-used entry.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000655 u_getbot();
656 curbuf->b_u_synced = FALSE;
657
Bram Moolenaare38eab22019-12-05 21:50:01 +0100658 // Move the found entry to become the last entry. The
659 // order of undo/redo doesn't matter for the entries
660 // we move it over, since they don't change the line
661 // count and don't include this line. It does matter
662 // for the found entry if the line count is changed by
663 // the executed command.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000664 prev_uep->ue_next = uep->ue_next;
665 uep->ue_next = curbuf->b_u_newhead->uh_entry;
666 curbuf->b_u_newhead->uh_entry = uep;
667 }
668
Bram Moolenaare38eab22019-12-05 21:50:01 +0100669 // The executed command may change the line count.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000670 if (newbot != 0)
671 uep->ue_bot = newbot;
672 else if (bot > curbuf->b_ml.ml_line_count)
673 uep->ue_bot = 0;
674 else
675 {
676 uep->ue_lcount = curbuf->b_ml.ml_line_count;
677 curbuf->b_u_newhead->uh_getbot_entry = uep;
678 }
679 return OK;
680 }
681 prev_uep = uep;
682 uep = uep->ue_next;
683 }
684 }
685
Bram Moolenaare38eab22019-12-05 21:50:01 +0100686 // find line number for ue_bot for previous u_save()
Bram Moolenaar071d4272004-06-13 20:20:40 +0000687 u_getbot();
688 }
689
Bram Moolenaar4f974752019-02-17 17:44:42 +0100690#if !defined(UNIX) && !defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000691 /*
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100692 * With Amiga we can't handle big undo's, because
Bram Moolenaar071d4272004-06-13 20:20:40 +0000693 * then u_alloc_line would have to allocate a block larger than 32K
694 */
695 if (size >= 8000)
696 goto nomem;
697#endif
698
699 /*
700 * add lines in front of entry list
701 */
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200702 uep = U_ALLOC_LINE(sizeof(u_entry_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000703 if (uep == NULL)
704 goto nomem;
Bram Moolenaara80faa82020-04-12 19:37:17 +0200705 CLEAR_POINTER(uep);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000706#ifdef U_DEBUG
707 uep->ue_magic = UE_MAGIC;
708#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000709
710 uep->ue_size = size;
711 uep->ue_top = top;
712 if (newbot != 0)
713 uep->ue_bot = newbot;
714 /*
715 * Use 0 for ue_bot if bot is below last line.
716 * Otherwise we have to compute ue_bot later.
717 */
718 else if (bot > curbuf->b_ml.ml_line_count)
719 uep->ue_bot = 0;
720 else
721 {
722 uep->ue_lcount = curbuf->b_ml.ml_line_count;
723 curbuf->b_u_newhead->uh_getbot_entry = uep;
724 }
725
Bram Moolenaar26a60b42005-02-22 08:49:11 +0000726 if (size > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000727 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200728 if ((uep->ue_array = U_ALLOC_LINE(sizeof(undoline_T) * size)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000729 {
730 u_freeentry(uep, 0L);
731 goto nomem;
732 }
733 for (i = 0, lnum = top + 1; i < size; ++i)
734 {
Bram Moolenaarae5bce12005-08-15 21:41:48 +0000735 fast_breakcheck();
736 if (got_int)
737 {
738 u_freeentry(uep, i);
739 return FAIL;
740 }
Bram Moolenaarccae4672019-01-04 15:09:57 +0100741 if (u_save_line(&uep->ue_array[i], lnum++) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000742 {
743 u_freeentry(uep, i);
744 goto nomem;
745 }
746 }
747 }
Bram Moolenaarf461c8e2005-06-25 23:04:51 +0000748 else
749 uep->ue_array = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000750 uep->ue_next = curbuf->b_u_newhead->uh_entry;
751 curbuf->b_u_newhead->uh_entry = uep;
752 curbuf->b_u_synced = FALSE;
753 undo_undoes = FALSE;
754
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000755#ifdef U_DEBUG
756 u_check(FALSE);
757#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000758 return OK;
759
760nomem:
Bram Moolenaare38eab22019-12-05 21:50:01 +0100761 msg_silent = 0; // must display the prompt
Bram Moolenaar071d4272004-06-13 20:20:40 +0000762 if (ask_yesno((char_u *)_("No undo possible; continue anyway"), TRUE)
763 == 'y')
764 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100765 undo_off = TRUE; // will be reset when character typed
Bram Moolenaar071d4272004-06-13 20:20:40 +0000766 return OK;
767 }
768 do_outofmem_msg((long_u)0);
769 return FAIL;
770}
771
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200772#if defined(FEAT_PERSISTENT_UNDO) || defined(PROTO)
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200773
Bram Moolenaare38eab22019-12-05 21:50:01 +0100774# define UF_START_MAGIC "Vim\237UnDo\345" // magic at start of undofile
Bram Moolenaar9db58062010-05-29 20:33:07 +0200775# define UF_START_MAGIC_LEN 9
Bram Moolenaare38eab22019-12-05 21:50:01 +0100776# define UF_HEADER_MAGIC 0x5fd0 // magic at start of header
777# define UF_HEADER_END_MAGIC 0xe7aa // magic after last header
778# define UF_ENTRY_MAGIC 0xf518 // magic at start of entry
779# define UF_ENTRY_END_MAGIC 0x3581 // magic after last entry
780# define UF_VERSION 2 // 2-byte undofile version number
781# define UF_VERSION_CRYPT 0x8002 // idem, encrypted
Bram Moolenaara800b422010-06-27 01:15:55 +0200782
Bram Moolenaare38eab22019-12-05 21:50:01 +0100783// extra fields for header
Bram Moolenaara800b422010-06-27 01:15:55 +0200784# define UF_LAST_SAVE_NR 1
785
Bram Moolenaare38eab22019-12-05 21:50:01 +0100786// extra fields for uhp
Bram Moolenaara800b422010-06-27 01:15:55 +0200787# define UHP_SAVE_NR 1
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200788
789/*
790 * Compute the hash for the current buffer text into hash[UNDO_HASH_SIZE].
791 */
792 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100793u_compute_hash(char_u *hash)
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200794{
795 context_sha256_T ctx;
796 linenr_T lnum;
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200797
798 sha256_start(&ctx);
Bram Moolenaarae7ba982011-12-08 15:14:09 +0100799 for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum)
John Marriottfd1a8382024-11-06 21:21:50 +0100800 sha256_update(&ctx, ml_get(lnum), (UINT32_T)(ml_get_len(lnum) + 1));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200801 sha256_finish(&ctx, hash);
802}
803
804/*
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200805 * Return an allocated string of the full path of the target undofile.
806 * When "reading" is TRUE find the file to read, go over all directories in
807 * 'undodir'.
808 * When "reading" is FALSE use the first name where the directory exists.
Bram Moolenaar9db58062010-05-29 20:33:07 +0200809 * Returns NULL when there is no place to write or no file to read.
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200810 */
Bram Moolenaar840d16f2019-09-10 21:27:18 +0200811 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100812u_get_undo_file_name(char_u *buf_ffname, int reading)
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200813{
814 char_u *dirp;
815 char_u dir_name[IOSIZE + 1];
816 char_u *munged_name = NULL;
817 char_u *undo_file_name = NULL;
818 int dir_len;
819 char_u *p;
John Marriottfd1a8382024-11-06 21:21:50 +0100820 size_t plen;
Bram Moolenaar8767f522016-07-01 17:17:39 +0200821 stat_T st;
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200822 char_u *ffname = buf_ffname;
John Marriottfd1a8382024-11-06 21:21:50 +0100823 size_t ffnamelen;
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200824#ifdef HAVE_READLINK
825 char_u fname_buf[MAXPATHL];
826#endif
827
828 if (ffname == NULL)
829 return NULL;
830
831#ifdef HAVE_READLINK
Bram Moolenaare38eab22019-12-05 21:50:01 +0100832 // Expand symlink in the file name, so that we put the undo file with the
833 // actual file instead of with the symlink.
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200834 if (resolve_symlink(ffname, fname_buf) == OK)
835 ffname = fname_buf;
836#endif
837
John Marriottfd1a8382024-11-06 21:21:50 +0100838 ffnamelen = STRLEN(ffname);
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~"
John Marriottfd1a8382024-11-06 21:21:50 +0100849 undo_file_name = vim_strnsave(ffname, ffnamelen + 5);
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200850 if (undo_file_name == NULL)
851 break;
852 p = gettail(undo_file_name);
John Marriottfd1a8382024-11-06 21:21:50 +0100853 plen = (size_t)(ffnamelen - (p - undo_file_name));
Bram Moolenaar206f0112014-03-12 16:51:55 +0100854#ifdef VMS
Bram Moolenaare38eab22019-12-05 21:50:01 +0100855 // VMS can not handle more than one dot in the filenames
856 // use "dir/name" -> "dir/_un_name" - add _un_
857 // at the beginning to keep the extension
John Marriottfd1a8382024-11-06 21:21:50 +0100858 mch_memmove(p + 4, p, plen + 1);
Bram Moolenaar206f0112014-03-12 16:51:55 +0100859 mch_memmove(p, "_un_", 4);
860
861#else
Bram Moolenaare38eab22019-12-05 21:50:01 +0100862 // Use same directory as the ffname,
863 // "dir/name" -> "dir/.name.un~"
John Marriottfd1a8382024-11-06 21:21:50 +0100864 mch_memmove(p + 1, p, plen + 1);
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200865 *p = '.';
John Marriottfd1a8382024-11-06 21:21:50 +0100866 STRCPY(p + plen + 1, ".un~");
Bram Moolenaar206f0112014-03-12 16:51:55 +0100867#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200868 }
869 else
870 {
871 dir_name[dir_len] = NUL;
872 if (mch_isdir(dir_name))
873 {
874 if (munged_name == NULL)
875 {
John Marriottfd1a8382024-11-06 21:21:50 +0100876 munged_name = vim_strnsave(ffname, ffnamelen);
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200877 if (munged_name == NULL)
878 return NULL;
Bram Moolenaar91acfff2017-03-12 19:22:36 +0100879 for (p = munged_name; *p != NUL; MB_PTR_ADV(p))
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200880 if (vim_ispathsep(*p))
881 *p = '%';
882 }
883 undo_file_name = concat_fnames(dir_name, munged_name, TRUE);
884 }
885 }
886
Bram Moolenaare38eab22019-12-05 21:50:01 +0100887 // When reading check if the file exists.
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200888 if (undo_file_name != NULL && (!reading
889 || mch_stat((char *)undo_file_name, &st) >= 0))
890 break;
Bram Moolenaard23a8232018-02-10 18:45:26 +0100891 VIM_CLEAR(undo_file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200892 }
893
894 vim_free(munged_name);
895 return undo_file_name;
896}
897
Bram Moolenaar9db58062010-05-29 20:33:07 +0200898 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100899corruption_error(char *mesg, char_u *file_name)
Bram Moolenaar9db58062010-05-29 20:33:07 +0200900{
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +0000901 semsg(_(e_corrupted_undo_file_str_str), mesg, file_name);
Bram Moolenaar9db58062010-05-29 20:33:07 +0200902}
903
904 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100905u_free_uhp(u_header_T *uhp)
Bram Moolenaar9db58062010-05-29 20:33:07 +0200906{
907 u_entry_T *nuep;
908 u_entry_T *uep;
909
910 uep = uhp->uh_entry;
911 while (uep != NULL)
912 {
913 nuep = uep->ue_next;
914 u_freeentry(uep, uep->ue_size);
915 uep = nuep;
916 }
917 vim_free(uhp);
918}
919
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200920/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200921 * Write a sequence of bytes to the undo file.
922 * Buffers and encrypts as needed.
923 * Returns OK or FAIL.
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200924 */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200925 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100926undo_write(bufinfo_T *bi, char_u *ptr, size_t len)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200927{
928#ifdef FEAT_CRYPT
929 if (bi->bi_buffer != NULL)
930 {
931 size_t len_todo = len;
932 char_u *p = ptr;
933
934 while (bi->bi_used + len_todo >= CRYPT_BUF_SIZE)
935 {
936 size_t n = CRYPT_BUF_SIZE - bi->bi_used;
937
938 mch_memmove(bi->bi_buffer + bi->bi_used, p, n);
939 len_todo -= n;
940 p += n;
941 bi->bi_used = CRYPT_BUF_SIZE;
942 if (undo_flush(bi) == FAIL)
943 return FAIL;
944 }
945 if (len_todo > 0)
946 {
947 mch_memmove(bi->bi_buffer + bi->bi_used, p, len_todo);
948 bi->bi_used += len_todo;
949 }
950 return OK;
951 }
952#endif
953 if (fwrite(ptr, len, (size_t)1, bi->bi_fp) != 1)
954 return FAIL;
955 return OK;
956}
957
958#ifdef FEAT_CRYPT
959 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100960undo_flush(bufinfo_T *bi)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200961{
Bram Moolenaar829aa642017-08-23 22:32:35 +0200962 if (bi->bi_buffer != NULL && bi->bi_state != NULL && bi->bi_used > 0)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200963 {
Christian Brabandtf573c6e2021-06-20 14:02:16 +0200964 // Last parameter is only used for sodium encryption and that
965 // explicitly disables encryption of undofiles.
966 crypt_encode_inplace(bi->bi_state, bi->bi_buffer, bi->bi_used, FALSE);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200967 if (fwrite(bi->bi_buffer, bi->bi_used, (size_t)1, bi->bi_fp) != 1)
968 return FAIL;
969 bi->bi_used = 0;
970 }
971 return OK;
972}
973#endif
974
975/*
976 * Write "ptr[len]" and crypt the bytes when needed.
977 * Returns OK or FAIL.
978 */
979 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100980fwrite_crypt(bufinfo_T *bi, char_u *ptr, size_t len)
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200981{
982#ifdef FEAT_CRYPT
983 char_u *copy;
984 char_u small_buf[100];
985 size_t i;
986
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200987 if (bi->bi_state != NULL && bi->bi_buffer == NULL)
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200988 {
Bram Moolenaare38eab22019-12-05 21:50:01 +0100989 // crypting every piece of text separately
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200990 if (len < 100)
Bram Moolenaare38eab22019-12-05 21:50:01 +0100991 copy = small_buf; // no malloc()/free() for short strings
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200992 else
993 {
994 copy = lalloc(len, FALSE);
995 if (copy == NULL)
996 return 0;
997 }
Christian Brabandtf573c6e2021-06-20 14:02:16 +0200998 // Last parameter is only used for sodium encryption and that
999 // explicitly disables encryption of undofiles.
1000 crypt_encode(bi->bi_state, ptr, len, copy, TRUE);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001001 i = fwrite(copy, len, (size_t)1, bi->bi_fp);
1002 if (copy != small_buf)
1003 vim_free(copy);
1004 return i == 1 ? OK : FAIL;
Bram Moolenaar191e0a22010-06-14 01:39:13 +02001005 }
Bram Moolenaar191e0a22010-06-14 01:39:13 +02001006#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001007 return undo_write(bi, ptr, len);
Bram Moolenaar191e0a22010-06-14 01:39:13 +02001008}
1009
1010/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001011 * Write a number, MSB first, in "len" bytes.
1012 * Must match with undo_read_?c() functions.
1013 * Returns OK or FAIL.
Bram Moolenaar191e0a22010-06-14 01:39:13 +02001014 */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001015 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001016undo_write_bytes(bufinfo_T *bi, long_u nr, int len)
Bram Moolenaar191e0a22010-06-14 01:39:13 +02001017{
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001018 char_u buf[8];
1019 int i;
1020 int bufi = 0;
Bram Moolenaar191e0a22010-06-14 01:39:13 +02001021
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001022 for (i = len - 1; i >= 0; --i)
Bram Moolenaar9b8f0212014-08-13 22:05:53 +02001023 buf[bufi++] = (char_u)(nr >> (i * 8));
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001024 return undo_write(bi, buf, (size_t)len);
1025}
1026
1027/*
1028 * Write the pointer to an undo header. Instead of writing the pointer itself
1029 * we use the sequence number of the header. This is converted back to
1030 * pointers when reading. */
1031 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001032put_header_ptr(bufinfo_T *bi, u_header_T *uhp)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001033{
1034 undo_write_bytes(bi, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
Bram Moolenaar191e0a22010-06-14 01:39:13 +02001035}
1036
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001037 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001038undo_read_4c(bufinfo_T *bi)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001039{
1040#ifdef FEAT_CRYPT
1041 if (bi->bi_buffer != NULL)
1042 {
1043 char_u buf[4];
1044 int n;
1045
1046 undo_read(bi, buf, (size_t)4);
Bram Moolenaar35169282014-09-11 22:50:09 +02001047 n = ((unsigned)buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3];
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001048 return n;
1049 }
1050#endif
1051 return get4c(bi->bi_fp);
1052}
1053
1054 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001055undo_read_2c(bufinfo_T *bi)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001056{
1057#ifdef FEAT_CRYPT
1058 if (bi->bi_buffer != NULL)
1059 {
1060 char_u buf[2];
1061 int n;
1062
1063 undo_read(bi, buf, (size_t)2);
1064 n = (buf[0] << 8) + buf[1];
1065 return n;
1066 }
1067#endif
1068 return get2c(bi->bi_fp);
1069}
1070
1071 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001072undo_read_byte(bufinfo_T *bi)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001073{
1074#ifdef FEAT_CRYPT
1075 if (bi->bi_buffer != NULL)
1076 {
1077 char_u buf[1];
1078
1079 undo_read(bi, buf, (size_t)1);
1080 return buf[0];
1081 }
1082#endif
1083 return getc(bi->bi_fp);
1084}
1085
1086 static time_t
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001087undo_read_time(bufinfo_T *bi)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001088{
1089#ifdef FEAT_CRYPT
1090 if (bi->bi_buffer != NULL)
1091 {
1092 char_u buf[8];
1093 time_t n = 0;
1094 int i;
1095
1096 undo_read(bi, buf, (size_t)8);
1097 for (i = 0; i < 8; ++i)
1098 n = (n << 8) + buf[i];
1099 return n;
1100 }
1101#endif
1102 return get8ctime(bi->bi_fp);
1103}
1104
1105/*
1106 * Read "buffer[size]" from the undo file.
1107 * Return OK or FAIL.
1108 */
1109 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001110undo_read(bufinfo_T *bi, char_u *buffer, size_t size)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001111{
Bram Moolenaar56f2db52017-06-11 23:09:15 +02001112 int retval = OK;
1113
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001114#ifdef FEAT_CRYPT
1115 if (bi->bi_buffer != NULL)
1116 {
Bram Moolenaar9b8f0212014-08-13 22:05:53 +02001117 int size_todo = (int)size;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001118 char_u *p = buffer;
1119
1120 while (size_todo > 0)
1121 {
1122 size_t n;
1123
1124 if (bi->bi_used >= bi->bi_avail)
1125 {
1126 n = fread(bi->bi_buffer, 1, (size_t)CRYPT_BUF_SIZE, bi->bi_fp);
Bram Moolenaar55952d42016-11-05 14:58:34 +01001127 if (n == 0)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001128 {
Bram Moolenaar56f2db52017-06-11 23:09:15 +02001129 retval = FAIL;
1130 break;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001131 }
1132 bi->bi_avail = n;
1133 bi->bi_used = 0;
Christian Brabandtf573c6e2021-06-20 14:02:16 +02001134 crypt_decode_inplace(bi->bi_state, bi->bi_buffer, bi->bi_avail, FALSE);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001135 }
1136 n = size_todo;
1137 if (n > bi->bi_avail - bi->bi_used)
1138 n = bi->bi_avail - bi->bi_used;
1139 mch_memmove(p, bi->bi_buffer + bi->bi_used, n);
1140 bi->bi_used += n;
Bram Moolenaar9b8f0212014-08-13 22:05:53 +02001141 size_todo -= (int)n;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001142 p += n;
1143 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001144 }
Bram Moolenaar56f2db52017-06-11 23:09:15 +02001145 else
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001146#endif
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00001147 if (fread(buffer, size, 1, bi->bi_fp) != 1)
Bram Moolenaar56f2db52017-06-11 23:09:15 +02001148 retval = FAIL;
1149
1150 if (retval == FAIL)
Bram Moolenaare38eab22019-12-05 21:50:01 +01001151 // Error may be checked for only later. Fill with zeros,
1152 // so that the reader won't use garbage.
Bram Moolenaar56f2db52017-06-11 23:09:15 +02001153 vim_memset(buffer, 0, size);
1154 return retval;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001155}
1156
1157/*
1158 * Read a string of length "len" from "bi->bi_fd".
1159 * "len" can be zero to allocate an empty line.
1160 * Decrypt the bytes if needed.
1161 * Append a NUL.
1162 * Returns a pointer to allocated memory or NULL for failure.
1163 */
1164 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001165read_string_decrypt(bufinfo_T *bi, int len)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001166{
Bram Moolenaar964b3742019-05-24 18:54:09 +02001167 char_u *ptr = alloc(len + 1);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001168
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00001169 if (ptr == NULL)
1170 return NULL;
1171
1172 if (len > 0 && undo_read(bi, ptr, len) == FAIL)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001173 {
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00001174 vim_free(ptr);
1175 return NULL;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001176 }
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00001177 // In case there are text properties there already is a NUL, but
1178 // checking for that is more expensive than just adding a dummy byte.
1179 ptr[len] = NUL;
1180#ifdef FEAT_CRYPT
1181 if (bi->bi_state != NULL && bi->bi_buffer == NULL)
1182 crypt_decode_inplace(bi->bi_state, ptr, len, FALSE);
1183#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001184 return ptr;
1185}
1186
1187/*
1188 * Writes the (not encrypted) header and initializes encryption if needed.
1189 */
1190 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001191serialize_header(bufinfo_T *bi, char_u *hash)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001192{
Bram 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;
John Marriottfd1a8382024-11-06 21:21:50 +01001208 long len;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001209
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001210 undo_write_bytes(bi, (long_u)UF_VERSION_CRYPT, 2);
1211 bi->bi_state = crypt_create_for_writing(crypt_get_method_nr(buf),
1212 buf->b_p_key, &header, &header_len);
1213 if (bi->bi_state == NULL)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001214 return FAIL;
Bram Moolenaarccae4672019-01-04 15:09:57 +01001215 len = (long)fwrite(header, (size_t)header_len, (size_t)1, fp);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001216 vim_free(header);
1217 if (len != 1)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001218 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001219 crypt_free_state(bi->bi_state);
1220 bi->bi_state = NULL;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001221 return FAIL;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001222 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001223
1224 if (crypt_whole_undofile(crypt_get_method_nr(buf)))
1225 {
1226 bi->bi_buffer = alloc(CRYPT_BUF_SIZE);
1227 if (bi->bi_buffer == NULL)
1228 {
1229 crypt_free_state(bi->bi_state);
1230 bi->bi_state = NULL;
1231 return FAIL;
1232 }
1233 bi->bi_used = 0;
1234 }
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001235 }
1236 else
1237#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001238 undo_write_bytes(bi, (long_u)UF_VERSION, 2);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001239
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001240
Bram Moolenaare38eab22019-12-05 21:50:01 +01001241 // Write a hash of the buffer text, so that we can verify it is still the
1242 // same when reading the buffer text.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001243 if (undo_write(bi, hash, (size_t)UNDO_HASH_SIZE) == FAIL)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001244 return FAIL;
1245
Bram Moolenaare38eab22019-12-05 21:50:01 +01001246 // buffer-specific data
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001247 undo_write_bytes(bi, (long_u)buf->b_ml.ml_line_count, 4);
John Marriottfd1a8382024-11-06 21:21:50 +01001248 undo_write_bytes(bi, (long_u)buf->b_u_line_ptr.ul_textlen, 4);
1249 if (buf->b_u_line_ptr.ul_textlen > 0 && fwrite_crypt(bi, buf->b_u_line_ptr.ul_line,
1250 (size_t)buf->b_u_line_ptr.ul_textlen) == FAIL)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001251 return FAIL;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001252 undo_write_bytes(bi, (long_u)buf->b_u_line_lnum, 4);
1253 undo_write_bytes(bi, (long_u)buf->b_u_line_colnr, 4);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001254
Bram Moolenaare38eab22019-12-05 21:50:01 +01001255 // Undo structures header data
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001256 put_header_ptr(bi, buf->b_u_oldhead);
1257 put_header_ptr(bi, buf->b_u_newhead);
1258 put_header_ptr(bi, buf->b_u_curhead);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001259
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001260 undo_write_bytes(bi, (long_u)buf->b_u_numhead, 4);
1261 undo_write_bytes(bi, (long_u)buf->b_u_seq_last, 4);
1262 undo_write_bytes(bi, (long_u)buf->b_u_seq_cur, 4);
1263 time_to_bytes(buf->b_u_time_cur, time_buf);
1264 undo_write(bi, time_buf, 8);
Bram Moolenaara800b422010-06-27 01:15:55 +02001265
Bram Moolenaare38eab22019-12-05 21:50:01 +01001266 // Optional fields.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001267 undo_write_bytes(bi, 4, 1);
1268 undo_write_bytes(bi, UF_LAST_SAVE_NR, 1);
1269 undo_write_bytes(bi, (long_u)buf->b_u_save_nr_last, 4);
Bram Moolenaara800b422010-06-27 01:15:55 +02001270
Bram Moolenaare38eab22019-12-05 21:50:01 +01001271 undo_write_bytes(bi, 0, 1); // end marker
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001272
1273 return OK;
1274}
1275
1276 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001277serialize_uhp(bufinfo_T *bi, u_header_T *uhp)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001278{
1279 int i;
1280 u_entry_T *uep;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001281 char_u time_buf[8];
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001282
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001283 if (undo_write_bytes(bi, (long_u)UF_HEADER_MAGIC, 2) == FAIL)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001284 return FAIL;
1285
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001286 put_header_ptr(bi, uhp->uh_next.ptr);
1287 put_header_ptr(bi, uhp->uh_prev.ptr);
1288 put_header_ptr(bi, uhp->uh_alt_next.ptr);
1289 put_header_ptr(bi, uhp->uh_alt_prev.ptr);
1290 undo_write_bytes(bi, uhp->uh_seq, 4);
1291 serialize_pos(bi, uhp->uh_cursor);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001292 undo_write_bytes(bi, (long_u)uhp->uh_cursor_vcol, 4);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001293 undo_write_bytes(bi, (long_u)uhp->uh_flags, 2);
Bram Moolenaare38eab22019-12-05 21:50:01 +01001294 // Assume NMARKS will stay the same.
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001295 for (i = 0; i < NMARKS; ++i)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001296 serialize_pos(bi, uhp->uh_namedm[i]);
1297 serialize_visualinfo(bi, &uhp->uh_visual);
1298 time_to_bytes(uhp->uh_time, time_buf);
1299 undo_write(bi, time_buf, 8);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001300
Bram Moolenaare38eab22019-12-05 21:50:01 +01001301 // Optional fields.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001302 undo_write_bytes(bi, 4, 1);
1303 undo_write_bytes(bi, UHP_SAVE_NR, 1);
1304 undo_write_bytes(bi, (long_u)uhp->uh_save_nr, 4);
Bram Moolenaara800b422010-06-27 01:15:55 +02001305
Bram Moolenaare38eab22019-12-05 21:50:01 +01001306 undo_write_bytes(bi, 0, 1); // end marker
Bram Moolenaara800b422010-06-27 01:15:55 +02001307
Bram Moolenaare38eab22019-12-05 21:50:01 +01001308 // Write all the entries.
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001309 for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
1310 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001311 undo_write_bytes(bi, (long_u)UF_ENTRY_MAGIC, 2);
1312 if (serialize_uep(bi, uep) == FAIL)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001313 return FAIL;
1314 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001315 undo_write_bytes(bi, (long_u)UF_ENTRY_END_MAGIC, 2);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001316 return OK;
1317}
1318
1319 static u_header_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001320unserialize_uhp(bufinfo_T *bi, char_u *file_name)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001321{
1322 u_header_T *uhp;
1323 int i;
1324 u_entry_T *uep, *last_uep;
1325 int c;
1326 int error;
1327
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001328 uhp = U_ALLOC_LINE(sizeof(u_header_T));
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001329 if (uhp == NULL)
1330 return NULL;
Bram Moolenaara80faa82020-04-12 19:37:17 +02001331 CLEAR_POINTER(uhp);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001332#ifdef U_DEBUG
1333 uhp->uh_magic = UH_MAGIC;
1334#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001335 uhp->uh_next.seq = undo_read_4c(bi);
1336 uhp->uh_prev.seq = undo_read_4c(bi);
1337 uhp->uh_alt_next.seq = undo_read_4c(bi);
1338 uhp->uh_alt_prev.seq = undo_read_4c(bi);
1339 uhp->uh_seq = undo_read_4c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001340 if (uhp->uh_seq <= 0)
1341 {
1342 corruption_error("uh_seq", file_name);
1343 vim_free(uhp);
1344 return NULL;
1345 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001346 unserialize_pos(bi, &uhp->uh_cursor);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001347 uhp->uh_cursor_vcol = undo_read_4c(bi);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001348 uhp->uh_flags = undo_read_2c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001349 for (i = 0; i < NMARKS; ++i)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001350 unserialize_pos(bi, &uhp->uh_namedm[i]);
1351 unserialize_visualinfo(bi, &uhp->uh_visual);
1352 uhp->uh_time = undo_read_time(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001353
Bram Moolenaare38eab22019-12-05 21:50:01 +01001354 // Optional fields.
Bram Moolenaar69154f22010-07-18 21:42:34 +02001355 for (;;)
1356 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001357 int len = undo_read_byte(bi);
Bram Moolenaar69154f22010-07-18 21:42:34 +02001358 int what;
Bram Moolenaara800b422010-06-27 01:15:55 +02001359
Bram Moolenaarfb06d762019-08-04 18:55:35 +02001360 if (len == EOF)
1361 {
1362 corruption_error("truncated", file_name);
1363 u_free_uhp(uhp);
1364 return NULL;
1365 }
Bram Moolenaar69154f22010-07-18 21:42:34 +02001366 if (len == 0)
1367 break;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001368 what = undo_read_byte(bi);
Bram Moolenaar69154f22010-07-18 21:42:34 +02001369 switch (what)
1370 {
1371 case UHP_SAVE_NR:
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001372 uhp->uh_save_nr = undo_read_4c(bi);
Bram Moolenaara800b422010-06-27 01:15:55 +02001373 break;
Bram Moolenaar69154f22010-07-18 21:42:34 +02001374 default:
Bram Moolenaare38eab22019-12-05 21:50:01 +01001375 // field not supported, skip
Bram Moolenaar69154f22010-07-18 21:42:34 +02001376 while (--len >= 0)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001377 (void)undo_read_byte(bi);
Bram Moolenaara800b422010-06-27 01:15:55 +02001378 }
Bram Moolenaar69154f22010-07-18 21:42:34 +02001379 }
Bram Moolenaara800b422010-06-27 01:15:55 +02001380
Bram Moolenaare38eab22019-12-05 21:50:01 +01001381 // Unserialize the uep list.
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001382 last_uep = NULL;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001383 while ((c = undo_read_2c(bi)) == UF_ENTRY_MAGIC)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001384 {
1385 error = FALSE;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001386 uep = unserialize_uep(bi, &error, file_name);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001387 if (last_uep == NULL)
1388 uhp->uh_entry = uep;
1389 else
1390 last_uep->ue_next = uep;
1391 last_uep = uep;
1392 if (uep == NULL || error)
1393 {
1394 u_free_uhp(uhp);
1395 return NULL;
1396 }
1397 }
1398 if (c != UF_ENTRY_END_MAGIC)
1399 {
1400 corruption_error("entry end", file_name);
1401 u_free_uhp(uhp);
1402 return NULL;
1403 }
1404
1405 return uhp;
1406}
1407
Bram Moolenaar9db58062010-05-29 20:33:07 +02001408/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001409 * Serialize "uep".
Bram Moolenaar9db58062010-05-29 20:33:07 +02001410 */
1411 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001412serialize_uep(
1413 bufinfo_T *bi,
1414 u_entry_T *uep)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001415{
1416 int i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001417
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001418 undo_write_bytes(bi, (long_u)uep->ue_top, 4);
1419 undo_write_bytes(bi, (long_u)uep->ue_bot, 4);
1420 undo_write_bytes(bi, (long_u)uep->ue_lcount, 4);
1421 undo_write_bytes(bi, (long_u)uep->ue_size, 4);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001422 for (i = 0; i < uep->ue_size; ++i)
1423 {
Bram Moolenaarccae4672019-01-04 15:09:57 +01001424 // Text is written without the text properties, since we cannot restore
1425 // the text property types.
John Marriottfd1a8382024-11-06 21:21:50 +01001426 if (undo_write_bytes(bi, (long_u)uep->ue_array[i].ul_textlen, 4) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001427 return FAIL;
John Marriottfd1a8382024-11-06 21:21:50 +01001428 if (uep->ue_array[i].ul_textlen > 0
1429 && fwrite_crypt(bi, uep->ue_array[i].ul_line, uep->ue_array[i].ul_textlen) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001430 return FAIL;
1431 }
1432 return OK;
1433}
1434
1435 static u_entry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001436unserialize_uep(bufinfo_T *bi, int *error, char_u *file_name)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001437{
1438 int i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001439 u_entry_T *uep;
Bram Moolenaarccae4672019-01-04 15:09:57 +01001440 undoline_T *array = NULL;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001441 char_u *line;
1442 int line_len;
1443
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001444 uep = U_ALLOC_LINE(sizeof(u_entry_T));
Bram Moolenaar9db58062010-05-29 20:33:07 +02001445 if (uep == NULL)
1446 return NULL;
Bram Moolenaara80faa82020-04-12 19:37:17 +02001447 CLEAR_POINTER(uep);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001448#ifdef U_DEBUG
1449 uep->ue_magic = UE_MAGIC;
1450#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001451 uep->ue_top = undo_read_4c(bi);
1452 uep->ue_bot = undo_read_4c(bi);
1453 uep->ue_lcount = undo_read_4c(bi);
1454 uep->ue_size = undo_read_4c(bi);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001455 if (uep->ue_size > 0)
1456 {
Bram Moolenaar0c8485f2017-02-26 18:17:10 +01001457 if (uep->ue_size < LONG_MAX / (int)sizeof(char_u *))
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001458 array = U_ALLOC_LINE(sizeof(undoline_T) * uep->ue_size);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001459 if (array == NULL)
1460 {
1461 *error = TRUE;
1462 return uep;
1463 }
Bram Moolenaarccae4672019-01-04 15:09:57 +01001464 vim_memset(array, 0, sizeof(undoline_T) * uep->ue_size);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001465 }
1466 uep->ue_array = array;
1467
1468 for (i = 0; i < uep->ue_size; ++i)
1469 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001470 line_len = undo_read_4c(bi);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001471 if (line_len >= 0)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001472 line = read_string_decrypt(bi, line_len);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001473 else
1474 {
1475 line = NULL;
1476 corruption_error("line length", file_name);
1477 }
1478 if (line == NULL)
1479 {
1480 *error = TRUE;
1481 return uep;
1482 }
Bram Moolenaarccae4672019-01-04 15:09:57 +01001483 array[i].ul_line = line;
1484 array[i].ul_len = line_len + 1;
John Marriottfd1a8382024-11-06 21:21:50 +01001485 array[i].ul_textlen = line_len;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001486 }
1487 return uep;
1488}
1489
1490/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001491 * Serialize "pos".
Bram Moolenaar9db58062010-05-29 20:33:07 +02001492 */
1493 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001494serialize_pos(bufinfo_T *bi, pos_T pos)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001495{
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001496 undo_write_bytes(bi, (long_u)pos.lnum, 4);
1497 undo_write_bytes(bi, (long_u)pos.col, 4);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001498 undo_write_bytes(bi, (long_u)pos.coladd, 4);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001499}
1500
1501/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001502 * Unserialize the pos_T at the current position.
Bram Moolenaar9db58062010-05-29 20:33:07 +02001503 */
1504 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001505unserialize_pos(bufinfo_T *bi, pos_T *pos)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001506{
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001507 pos->lnum = undo_read_4c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001508 if (pos->lnum < 0)
1509 pos->lnum = 0;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001510 pos->col = undo_read_4c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001511 if (pos->col < 0)
1512 pos->col = 0;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001513 pos->coladd = undo_read_4c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001514 if (pos->coladd < 0)
1515 pos->coladd = 0;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001516}
1517
1518/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001519 * Serialize "info".
Bram Moolenaar9db58062010-05-29 20:33:07 +02001520 */
1521 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001522serialize_visualinfo(bufinfo_T *bi, visualinfo_T *info)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001523{
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001524 serialize_pos(bi, info->vi_start);
1525 serialize_pos(bi, info->vi_end);
1526 undo_write_bytes(bi, (long_u)info->vi_mode, 4);
1527 undo_write_bytes(bi, (long_u)info->vi_curswant, 4);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001528}
1529
1530/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001531 * Unserialize the visualinfo_T at the current position.
Bram Moolenaar9db58062010-05-29 20:33:07 +02001532 */
1533 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001534unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001535{
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001536 unserialize_pos(bi, &info->vi_start);
1537 unserialize_pos(bi, &info->vi_end);
1538 info->vi_mode = undo_read_4c(bi);
1539 info->vi_curswant = undo_read_4c(bi);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001540}
1541
1542/*
1543 * Write the undo tree in an undo file.
1544 * When "name" is not NULL, use it as the name of the undo file.
1545 * Otherwise use buf->b_ffname to generate the undo file name.
1546 * "buf" must never be null, buf->b_ffname is used to obtain the original file
1547 * permissions.
1548 * "forceit" is TRUE for ":wundo!", FALSE otherwise.
1549 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
1550 */
1551 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001552u_write_undo(
1553 char_u *name,
1554 int forceit,
1555 buf_T *buf,
1556 char_u *hash)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001557{
1558 u_header_T *uhp;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001559 char_u *file_name;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001560 int mark;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001561#ifdef U_DEBUG
1562 int headers_written = 0;
1563#endif
1564 int fd;
1565 FILE *fp = NULL;
1566 int perm;
1567 int write_ok = FALSE;
1568#ifdef UNIX
1569 int st_old_valid = FALSE;
Bram Moolenaar8767f522016-07-01 17:17:39 +02001570 stat_T st_old;
1571 stat_T st_new;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001572#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001573 bufinfo_T bi;
1574
Bram Moolenaara80faa82020-04-12 19:37:17 +02001575 CLEAR_FIELD(bi);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001576
1577 if (name == NULL)
1578 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001579 file_name = u_get_undo_file_name(buf->b_ffname, FALSE);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001580 if (file_name == NULL)
1581 {
1582 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001583 {
1584 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001585 smsg(
Bram Moolenaar504a8212010-05-30 17:17:42 +02001586 _("Cannot write undo file in any directory in 'undodir'"));
1587 verbose_leave();
1588 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001589 return;
1590 }
1591 }
1592 else
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001593 file_name = name;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001594
1595 /*
1596 * Decide about the permission to use for the undo file. If the buffer
1597 * has a name use the permission of the original file. Otherwise only
1598 * allow the user to access the undo file.
1599 */
1600 perm = 0600;
1601 if (buf->b_ffname != NULL)
1602 {
1603#ifdef UNIX
1604 if (mch_stat((char *)buf->b_ffname, &st_old) >= 0)
1605 {
1606 perm = st_old.st_mode;
1607 st_old_valid = TRUE;
1608 }
1609#else
1610 perm = mch_getperm(buf->b_ffname);
1611 if (perm < 0)
1612 perm = 0600;
1613#endif
1614 }
1615
Bram Moolenaare38eab22019-12-05 21:50:01 +01001616 // strip any s-bit and executable bit
Bram Moolenaar3cbac302015-04-21 16:12:06 +02001617 perm = perm & 0666;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001618
Bram Moolenaare38eab22019-12-05 21:50:01 +01001619 // If the undo file already exists, verify that it actually is an undo
1620 // file, and delete it.
Bram Moolenaar9db58062010-05-29 20:33:07 +02001621 if (mch_getperm(file_name) >= 0)
1622 {
1623 if (name == NULL || !forceit)
1624 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01001625 // Check we can read it and it's an undo file.
Bram Moolenaar9db58062010-05-29 20:33:07 +02001626 fd = mch_open((char *)file_name, O_RDONLY|O_EXTRA, 0);
1627 if (fd < 0)
1628 {
1629 if (name != NULL || p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001630 {
1631 if (name == NULL)
1632 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001633 smsg(
Bram Moolenaar504a8212010-05-30 17:17:42 +02001634 _("Will not overwrite with undo file, cannot read: %s"),
Bram Moolenaar9db58062010-05-29 20:33:07 +02001635 file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001636 if (name == NULL)
1637 verbose_leave();
1638 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001639 goto theend;
1640 }
1641 else
1642 {
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001643 char_u mbuf[UF_START_MAGIC_LEN];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001644 int len;
1645
Bram Moolenaar540fc6f2010-12-17 16:27:16 +01001646 len = read_eintr(fd, mbuf, UF_START_MAGIC_LEN);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001647 close(fd);
1648 if (len < UF_START_MAGIC_LEN
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001649 || memcmp(mbuf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001650 {
1651 if (name != NULL || p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001652 {
1653 if (name == NULL)
1654 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001655 smsg(
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001656 _("Will not overwrite, this is not an undo file: %s"),
Bram Moolenaar9db58062010-05-29 20:33:07 +02001657 file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001658 if (name == NULL)
1659 verbose_leave();
1660 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001661 goto theend;
1662 }
1663 }
1664 }
1665 mch_remove(file_name);
1666 }
1667
Bram Moolenaare38eab22019-12-05 21:50:01 +01001668 // If there is no undo information at all, quit here after deleting any
1669 // existing undo file.
Bram Moolenaarccae4672019-01-04 15:09:57 +01001670 if (buf->b_u_numhead == 0 && buf->b_u_line_ptr.ul_line == NULL)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001671 {
1672 if (p_verbose > 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001673 verb_msg(_("Skipping undo file write, nothing to undo"));
Bram Moolenaar504a8212010-05-30 17:17:42 +02001674 goto theend;
1675 }
1676
Bram Moolenaar9db58062010-05-29 20:33:07 +02001677 fd = mch_open((char *)file_name,
1678 O_CREAT|O_EXTRA|O_WRONLY|O_EXCL|O_NOFOLLOW, perm);
1679 if (fd < 0)
1680 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00001681 semsg(_(e_cannot_open_undo_file_for_writing_str), file_name);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001682 goto theend;
1683 }
1684 (void)mch_setperm(file_name, perm);
1685 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001686 {
1687 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001688 smsg(_("Writing undo file: %s"), file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001689 verbose_leave();
1690 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001691
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001692#ifdef U_DEBUG
Bram Moolenaare38eab22019-12-05 21:50:01 +01001693 // Check there is no problem in undo info before writing.
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001694 u_check(FALSE);
1695#endif
1696
Bram Moolenaar9db58062010-05-29 20:33:07 +02001697#ifdef UNIX
1698 /*
1699 * Try to set the group of the undo file same as the original file. If
1700 * this fails, set the protection bits for the group same as the
1701 * protection bits for others.
1702 */
Bram Moolenaarce69e822010-07-21 20:31:07 +02001703 if (st_old_valid
1704 && mch_stat((char *)file_name, &st_new) >= 0
1705 && st_new.st_gid != st_old.st_gid
Bram Moolenaare38eab22019-12-05 21:50:01 +01001706# ifdef HAVE_FCHOWN // sequent-ptx lacks fchown()
Bram Moolenaarce69e822010-07-21 20:31:07 +02001707 && fchown(fd, (uid_t)-1, st_old.st_gid) != 0
Bram Moolenaar9db58062010-05-29 20:33:07 +02001708# endif
1709 )
1710 mch_setperm(file_name, (perm & 0707) | ((perm & 07) << 3));
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02001711# if defined(HAVE_SELINUX) || defined(HAVE_SMACK)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001712 if (buf->b_ffname != NULL)
1713 mch_copy_sec(buf->b_ffname, file_name);
1714# endif
1715#endif
1716
1717 fp = fdopen(fd, "w");
1718 if (fp == NULL)
1719 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00001720 semsg(_(e_cannot_open_undo_file_for_writing_str), file_name);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001721 close(fd);
1722 mch_remove(file_name);
1723 goto theend;
1724 }
1725
Bram Moolenaare38eab22019-12-05 21:50:01 +01001726 // Undo must be synced.
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001727 u_sync(TRUE);
1728
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001729 /*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001730 * Write the header. Initializes encryption, if enabled.
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001731 */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001732 bi.bi_buf = buf;
1733 bi.bi_fp = fp;
1734 if (serialize_header(&bi, hash) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001735 goto write_error;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001736
1737 /*
1738 * Iteratively serialize UHPs and their UEPs from the top down.
1739 */
1740 mark = ++lastmark;
1741 uhp = buf->b_u_oldhead;
1742 while (uhp != NULL)
1743 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01001744 // Serialize current UHP if we haven't seen it
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001745 if (uhp->uh_walk != mark)
1746 {
1747 uhp->uh_walk = mark;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001748#ifdef U_DEBUG
1749 ++headers_written;
1750#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001751 if (serialize_uhp(&bi, uhp) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001752 goto write_error;
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001753 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001754
Bram Moolenaare38eab22019-12-05 21:50:01 +01001755 // Now walk through the tree - algorithm from undo_time().
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001756 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != mark)
1757 uhp = uhp->uh_prev.ptr;
1758 else if (uhp->uh_alt_next.ptr != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001759 && uhp->uh_alt_next.ptr->uh_walk != mark)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001760 uhp = uhp->uh_alt_next.ptr;
1761 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001762 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001763 uhp = uhp->uh_next.ptr;
1764 else if (uhp->uh_alt_prev.ptr != NULL)
1765 uhp = uhp->uh_alt_prev.ptr;
1766 else
1767 uhp = uhp->uh_next.ptr;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001768 }
1769
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001770 if (undo_write_bytes(&bi, (long_u)UF_HEADER_END_MAGIC, 2) == OK)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001771 write_ok = TRUE;
1772#ifdef U_DEBUG
1773 if (headers_written != buf->b_u_numhead)
Bram Moolenaar570064c2013-06-10 20:25:10 +02001774 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001775 semsg("Written %ld headers, ...", headers_written);
1776 semsg("... but numhead is %ld", buf->b_u_numhead);
Bram Moolenaar570064c2013-06-10 20:25:10 +02001777 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001778#endif
1779
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001780#ifdef FEAT_CRYPT
1781 if (bi.bi_state != NULL && undo_flush(&bi) == FAIL)
1782 write_ok = FALSE;
1783#endif
1784
Bram Moolenaar340dd0f2021-10-14 17:52:23 +01001785#if defined(UNIX) && defined(HAVE_FSYNC)
1786 if (p_fs && fflush(fp) == 0 && vim_fsync(fd) != 0)
1787 write_ok = FALSE;
1788#endif
1789
Bram Moolenaar9db58062010-05-29 20:33:07 +02001790write_error:
1791 fclose(fp);
1792 if (!write_ok)
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00001793 semsg(_(e_write_error_in_undo_file_str), file_name);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001794
Bram Moolenaar4f974752019-02-17 17:44:42 +01001795#if defined(MSWIN)
Bram Moolenaare38eab22019-12-05 21:50:01 +01001796 // Copy file attributes; for systems where this can only be done after
1797 // closing the file.
Bram Moolenaar9db58062010-05-29 20:33:07 +02001798 if (buf->b_ffname != NULL)
1799 (void)mch_copy_file_attribute(buf->b_ffname, file_name);
1800#endif
1801#ifdef HAVE_ACL
1802 if (buf->b_ffname != NULL)
1803 {
1804 vim_acl_T acl;
1805
Bram Moolenaare38eab22019-12-05 21:50:01 +01001806 // For systems that support ACL: get the ACL from the original file.
Bram Moolenaar9db58062010-05-29 20:33:07 +02001807 acl = mch_get_acl(buf->b_ffname);
1808 mch_set_acl(file_name, acl);
Bram Moolenaard2aed442012-06-01 13:46:12 +02001809 mch_free_acl(acl);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001810 }
1811#endif
1812
1813theend:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001814#ifdef FEAT_CRYPT
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001815 if (bi.bi_state != NULL)
1816 crypt_free_state(bi.bi_state);
1817 vim_free(bi.bi_buffer);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001818#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02001819 if (file_name != name)
1820 vim_free(file_name);
1821}
1822
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001823/*
1824 * Load the undo tree from an undo file.
1825 * If "name" is not NULL use it as the undo file name. This also means being
1826 * a bit more verbose.
1827 * Otherwise use curbuf->b_ffname to generate the undo file name.
1828 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
1829 */
1830 void
Bram Moolenaarbd67aac2019-09-21 23:09:04 +02001831u_read_undo(char_u *name, char_u *hash, char_u *orig_name UNUSED)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001832{
1833 char_u *file_name;
1834 FILE *fp;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001835 long version, str_len;
Bram Moolenaarccae4672019-01-04 15:09:57 +01001836 undoline_T line_ptr;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001837 linenr_T line_lnum;
1838 colnr_T line_colnr;
1839 linenr_T line_count;
Bram Moolenaar3eb16372017-02-26 18:11:36 +01001840 long num_head = 0;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001841 long old_header_seq, new_header_seq, cur_header_seq;
1842 long seq_last, seq_cur;
Bram Moolenaarb2c03502010-07-02 20:20:09 +02001843 long last_save_nr = 0;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001844 short old_idx = -1, new_idx = -1, cur_idx = -1;
1845 long num_read_uhps = 0;
1846 time_t seq_time;
1847 int i, j;
1848 int c;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001849 u_header_T *uhp;
1850 u_header_T **uhp_table = NULL;
1851 char_u read_hash[UNDO_HASH_SIZE];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001852 char_u magic_buf[UF_START_MAGIC_LEN];
1853#ifdef U_DEBUG
1854 int *uhp_table_used;
1855#endif
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001856#ifdef UNIX
Bram Moolenaar8767f522016-07-01 17:17:39 +02001857 stat_T st_orig;
1858 stat_T st_undo;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001859#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001860 bufinfo_T bi;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001861
Bram Moolenaara80faa82020-04-12 19:37:17 +02001862 CLEAR_FIELD(bi);
Bram Moolenaarccae4672019-01-04 15:09:57 +01001863 line_ptr.ul_len = 0;
John Marriottfd1a8382024-11-06 21:21:50 +01001864 line_ptr.ul_textlen = 0;
Bram Moolenaarccae4672019-01-04 15:09:57 +01001865 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;
John Marriottfd1a8382024-11-06 21:21:50 +01001989 line_ptr.ul_textlen = str_len;
Bram Moolenaarccae4672019-01-04 15:09:57 +01001990 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001991 line_lnum = (linenr_T)undo_read_4c(&bi);
1992 line_colnr = (colnr_T)undo_read_4c(&bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001993 if (line_lnum < 0 || line_colnr < 0)
1994 {
1995 corruption_error("line lnum/col", file_name);
1996 goto error;
1997 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001998
Bram Moolenaare38eab22019-12-05 21:50:01 +01001999 // Begin general undo data
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002000 old_header_seq = undo_read_4c(&bi);
2001 new_header_seq = undo_read_4c(&bi);
2002 cur_header_seq = undo_read_4c(&bi);
2003 num_head = undo_read_4c(&bi);
2004 seq_last = undo_read_4c(&bi);
2005 seq_cur = undo_read_4c(&bi);
2006 seq_time = undo_read_time(&bi);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002007
Bram Moolenaare38eab22019-12-05 21:50:01 +01002008 // Optional header fields.
Bram Moolenaar69154f22010-07-18 21:42:34 +02002009 for (;;)
2010 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002011 int len = undo_read_byte(&bi);
Bram Moolenaar69154f22010-07-18 21:42:34 +02002012 int what;
Bram Moolenaara800b422010-06-27 01:15:55 +02002013
Bram Moolenaar69154f22010-07-18 21:42:34 +02002014 if (len == 0 || len == EOF)
2015 break;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002016 what = undo_read_byte(&bi);
Bram Moolenaar69154f22010-07-18 21:42:34 +02002017 switch (what)
2018 {
2019 case UF_LAST_SAVE_NR:
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002020 last_save_nr = undo_read_4c(&bi);
Bram Moolenaara800b422010-06-27 01:15:55 +02002021 break;
Bram Moolenaar69154f22010-07-18 21:42:34 +02002022 default:
Bram Moolenaare38eab22019-12-05 21:50:01 +01002023 // field not supported, skip
Bram Moolenaar69154f22010-07-18 21:42:34 +02002024 while (--len >= 0)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002025 (void)undo_read_byte(&bi);
Bram Moolenaara800b422010-06-27 01:15:55 +02002026 }
Bram Moolenaar69154f22010-07-18 21:42:34 +02002027 }
Bram Moolenaara800b422010-06-27 01:15:55 +02002028
Bram Moolenaare38eab22019-12-05 21:50:01 +01002029 // uhp_table will store the freshly created undo headers we allocate
2030 // until we insert them into curbuf. The table remains sorted by the
2031 // sequence numbers of the headers.
2032 // When there are no headers uhp_table is NULL.
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002033 if (num_head > 0)
2034 {
Bram Moolenaar3eb16372017-02-26 18:11:36 +01002035 if (num_head < LONG_MAX / (long)sizeof(u_header_T *))
Bram Moolenaarc799fe22019-05-28 23:08:19 +02002036 uhp_table = U_ALLOC_LINE(num_head * sizeof(u_header_T *));
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002037 if (uhp_table == NULL)
2038 goto error;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002039 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002040
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002041 while ((c = undo_read_2c(&bi)) == UF_HEADER_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002042 {
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02002043 if (num_read_uhps >= num_head)
2044 {
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02002045 corruption_error("num_head too small", file_name);
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02002046 goto error;
2047 }
2048
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002049 uhp = unserialize_uhp(&bi, file_name);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02002050 if (uhp == NULL)
2051 goto error;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02002052 uhp_table[num_read_uhps++] = uhp;
2053 }
2054
2055 if (num_read_uhps != num_head)
2056 {
2057 corruption_error("num_head", file_name);
2058 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002059 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02002060 if (c != UF_HEADER_END_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002061 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02002062 corruption_error("end marker", file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002063 goto error;
2064 }
2065
Bram Moolenaar9db58062010-05-29 20:33:07 +02002066#ifdef U_DEBUG
Bram Moolenaarc799fe22019-05-28 23:08:19 +02002067 uhp_table_used = alloc_clear(sizeof(int) * num_head + 1);
Bram Moolenaar9db58062010-05-29 20:33:07 +02002068# define SET_FLAG(j) ++uhp_table_used[j]
2069#else
2070# define SET_FLAG(j)
2071#endif
2072
Bram Moolenaare38eab22019-12-05 21:50:01 +01002073 // We have put all of the headers into a table. Now we iterate through the
2074 // table and swizzle each sequence number we have stored in uh_*_seq into
2075 // a pointer corresponding to the header with that sequence number.
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002076 for (i = 0; i < num_head; i++)
2077 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002078 uhp = uhp_table[i];
2079 if (uhp == NULL)
2080 continue;
2081 for (j = 0; j < num_head; j++)
2082 if (uhp_table[j] != NULL && i != j
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002083 && uhp_table[i]->uh_seq == uhp_table[j]->uh_seq)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002084 {
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02002085 corruption_error("duplicate uh_seq", file_name);
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002086 goto error;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02002087 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002088 for (j = 0; j < num_head; j++)
2089 if (uhp_table[j] != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002090 && uhp_table[j]->uh_seq == uhp->uh_next.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002091 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002092 uhp->uh_next.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02002093 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002094 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002095 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002096 for (j = 0; j < num_head; j++)
2097 if (uhp_table[j] != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002098 && uhp_table[j]->uh_seq == uhp->uh_prev.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002099 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002100 uhp->uh_prev.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02002101 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002102 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002103 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002104 for (j = 0; j < num_head; j++)
2105 if (uhp_table[j] != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002106 && uhp_table[j]->uh_seq == uhp->uh_alt_next.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002107 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002108 uhp->uh_alt_next.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02002109 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002110 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002111 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002112 for (j = 0; j < num_head; j++)
2113 if (uhp_table[j] != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002114 && uhp_table[j]->uh_seq == uhp->uh_alt_prev.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002115 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002116 uhp->uh_alt_prev.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02002117 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002118 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002119 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002120 if (old_header_seq > 0 && old_idx < 0 && uhp->uh_seq == old_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002121 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002122 old_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002123 SET_FLAG(i);
2124 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002125 if (new_header_seq > 0 && new_idx < 0 && uhp->uh_seq == new_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002126 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002127 new_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002128 SET_FLAG(i);
2129 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002130 if (cur_header_seq > 0 && cur_idx < 0 && uhp->uh_seq == cur_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002131 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002132 cur_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002133 SET_FLAG(i);
2134 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002135 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02002136
Bram Moolenaare38eab22019-12-05 21:50:01 +01002137 // Now that we have read the undo info successfully, free the current undo
2138 // info and use the info from the file.
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002139 u_blockfree(curbuf);
Bram Moolenaar9db58062010-05-29 20:33:07 +02002140 curbuf->b_u_oldhead = old_idx < 0 ? NULL : uhp_table[old_idx];
2141 curbuf->b_u_newhead = new_idx < 0 ? NULL : uhp_table[new_idx];
2142 curbuf->b_u_curhead = cur_idx < 0 ? NULL : uhp_table[cur_idx];
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002143 curbuf->b_u_line_ptr = line_ptr;
2144 curbuf->b_u_line_lnum = line_lnum;
2145 curbuf->b_u_line_colnr = line_colnr;
2146 curbuf->b_u_numhead = num_head;
2147 curbuf->b_u_seq_last = seq_last;
2148 curbuf->b_u_seq_cur = seq_cur;
Bram Moolenaara800b422010-06-27 01:15:55 +02002149 curbuf->b_u_time_cur = seq_time;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002150 curbuf->b_u_save_nr_last = last_save_nr;
Bram Moolenaardba01a02010-11-03 19:32:42 +01002151 curbuf->b_u_save_nr_cur = last_save_nr;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02002152
2153 curbuf->b_u_synced = TRUE;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002154 vim_free(uhp_table);
Bram Moolenaar9db58062010-05-29 20:33:07 +02002155
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002156#ifdef U_DEBUG
Bram Moolenaar9db58062010-05-29 20:33:07 +02002157 for (i = 0; i < num_head; ++i)
2158 if (uhp_table_used[i] == 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002159 semsg("uhp_table entry %ld not used, leaking memory", i);
Bram Moolenaar9db58062010-05-29 20:33:07 +02002160 vim_free(uhp_table_used);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002161 u_check(TRUE);
2162#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02002163
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002164 if (name != NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002165 smsg(_("Finished reading undo file %s"), file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002166 goto theend;
2167
2168error:
Bram Moolenaarccae4672019-01-04 15:09:57 +01002169 vim_free(line_ptr.ul_line);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002170 if (uhp_table != NULL)
2171 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002172 for (i = 0; i < num_read_uhps; i++)
2173 if (uhp_table[i] != NULL)
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02002174 u_free_uhp(uhp_table[i]);
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002175 vim_free(uhp_table);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002176 }
2177
2178theend:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002179#ifdef FEAT_CRYPT
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002180 if (bi.bi_state != NULL)
2181 crypt_free_state(bi.bi_state);
2182 vim_free(bi.bi_buffer);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002183#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002184 if (fp != NULL)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002185 fclose(fp);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002186 if (file_name != name)
2187 vim_free(file_name);
2188 return;
2189}
2190
Bram Moolenaare38eab22019-12-05 21:50:01 +01002191#endif // FEAT_PERSISTENT_UNDO
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002192
2193
Bram Moolenaar071d4272004-06-13 20:20:40 +00002194/*
2195 * If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible).
2196 * If 'cpoptions' does not contain 'u': Always undo.
2197 */
2198 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002199u_undo(int count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002200{
2201 /*
2202 * If we get an undo command while executing a macro, we behave like the
2203 * original vi. If this happens twice in one macro the result will not
2204 * be compatible.
2205 */
2206 if (curbuf->b_u_synced == FALSE)
2207 {
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002208 u_sync(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002209 count = 1;
2210 }
2211
2212 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
2213 undo_undoes = TRUE;
2214 else
2215 undo_undoes = !undo_undoes;
2216 u_doit(count);
2217}
2218
2219/*
2220 * If 'cpoptions' contains 'u': Repeat the previous undo or redo.
2221 * If 'cpoptions' does not contain 'u': Always redo.
2222 */
2223 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002224u_redo(int count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002225{
2226 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
2227 undo_undoes = FALSE;
2228 u_doit(count);
2229}
2230
2231/*
2232 * Undo or redo, depending on 'undo_undoes', 'count' times.
2233 */
2234 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002235u_doit(int startcount)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002236{
Bram Moolenaarca003e12006-03-17 23:19:38 +00002237 int count = startcount;
2238
Bram Moolenaar8ada17c2006-01-19 22:16:24 +00002239 if (!undo_allowed())
Bram Moolenaar071d4272004-06-13 20:20:40 +00002240 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002241
2242 u_newcount = 0;
2243 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00002244 if (curbuf->b_ml.ml_flags & ML_EMPTY)
2245 u_oldcount = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002246 while (count--)
2247 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002248 // Do the change warning now, so that it triggers FileChangedRO when
2249 // needed. This may cause the file to be reloaded, that must happen
2250 // before we do anything, because it may change curbuf->b_u_curhead
2251 // and more.
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002252 change_warning(0);
2253
Bram Moolenaar071d4272004-06-13 20:20:40 +00002254 if (undo_undoes)
2255 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002256 if (curbuf->b_u_curhead == NULL) // first undo
Bram Moolenaar071d4272004-06-13 20:20:40 +00002257 curbuf->b_u_curhead = curbuf->b_u_newhead;
Bram Moolenaare38eab22019-12-05 21:50:01 +01002258 else if (get_undolevel() > 0) // multi level undo
2259 // get next undo
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002260 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaare38eab22019-12-05 21:50:01 +01002261 // nothing to undo
Bram Moolenaar071d4272004-06-13 20:20:40 +00002262 if (curbuf->b_u_numhead == 0 || curbuf->b_u_curhead == NULL)
2263 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002264 // stick curbuf->b_u_curhead at end
Bram Moolenaar071d4272004-06-13 20:20:40 +00002265 curbuf->b_u_curhead = curbuf->b_u_oldhead;
2266 beep_flush();
Bram Moolenaarca003e12006-03-17 23:19:38 +00002267 if (count == startcount - 1)
2268 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002269 msg(_("Already at oldest change"));
Bram Moolenaarca003e12006-03-17 23:19:38 +00002270 return;
2271 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002272 break;
2273 }
2274
Bram Moolenaarca003e12006-03-17 23:19:38 +00002275 u_undoredo(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002276 }
2277 else
2278 {
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +01002279 if (curbuf->b_u_curhead == NULL || get_undolevel() <= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002280 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002281 beep_flush(); // nothing to redo
Bram Moolenaarca003e12006-03-17 23:19:38 +00002282 if (count == startcount - 1)
2283 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002284 msg(_("Already at newest change"));
Bram Moolenaarca003e12006-03-17 23:19:38 +00002285 return;
2286 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002287 break;
2288 }
2289
Bram Moolenaarca003e12006-03-17 23:19:38 +00002290 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002291
Bram Moolenaare38eab22019-12-05 21:50:01 +01002292 // Advance for next redo. Set "newhead" when at the end of the
2293 // redoable changes.
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002294 if (curbuf->b_u_curhead->uh_prev.ptr == NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002295 curbuf->b_u_newhead = curbuf->b_u_curhead;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002296 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002297 }
2298 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00002299 u_undo_end(undo_undoes, FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002300}
2301
Bram Moolenaar1e607892006-03-13 22:15:53 +00002302/*
2303 * Undo or redo over the timeline.
2304 * When "step" is negative go back in time, otherwise goes forward in time.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002305 * When "sec" is FALSE make "step" steps, when "sec" is TRUE use "step" as
2306 * seconds.
Bram Moolenaar730cde92010-06-27 05:18:54 +02002307 * When "file" is TRUE use "step" as a number of file writes.
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002308 * When "absolute" is TRUE use "step" as the sequence number to jump to.
2309 * "sec" must be FALSE then.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002310 */
2311 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002312undo_time(
2313 long step,
2314 int sec,
2315 int file,
2316 int absolute)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002317{
2318 long target;
2319 long closest;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002320 long closest_start;
2321 long closest_seq = 0;
2322 long val;
Bram Moolenaarce46d932018-01-30 22:46:06 +01002323 u_header_T *uhp = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002324 u_header_T *last;
2325 int mark;
Bram Moolenaar1f3601e2019-04-26 20:33:00 +02002326 int nomark = 0; // shut up compiler
Bram Moolenaar1e607892006-03-13 22:15:53 +00002327 int round;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002328 int dosec = sec;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002329 int dofile = file;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002330 int above = FALSE;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002331 int did_undo = TRUE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002332
Bram Moolenaar338f1fc2022-05-26 15:56:23 +01002333 if (text_locked())
2334 {
2335 text_locked_msg();
2336 return;
2337 }
2338
Bram Moolenaare38eab22019-12-05 21:50:01 +01002339 // First make sure the current undoable change is synced.
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00002340 if (curbuf->b_u_synced == FALSE)
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002341 u_sync(TRUE);
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00002342
Bram Moolenaar1e607892006-03-13 22:15:53 +00002343 u_newcount = 0;
2344 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00002345 if (curbuf->b_ml.ml_flags & ML_EMPTY)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002346 u_oldcount = -1;
2347
Bram Moolenaare38eab22019-12-05 21:50:01 +01002348 // "target" is the node below which we want to be.
2349 // Init "closest" to a value we can't reach.
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002350 if (absolute)
2351 {
Bram Moolenaarce46d932018-01-30 22:46:06 +01002352 target = step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002353 closest = -1;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002354 }
Bram Moolenaarca003e12006-03-17 23:19:38 +00002355 else
Bram Moolenaar1e607892006-03-13 22:15:53 +00002356 {
Bram Moolenaar730cde92010-06-27 05:18:54 +02002357 if (dosec)
Bram Moolenaarcbd4de42017-01-07 16:14:57 +01002358 target = (long)(curbuf->b_u_time_cur) + step;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002359 else if (dofile)
2360 {
2361 if (step < 0)
2362 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002363 // Going back to a previous write. If there were changes after
2364 // the last write, count that as moving one file-write, so
2365 // that ":earlier 1f" undoes all changes since the last save.
Bram Moolenaar730cde92010-06-27 05:18:54 +02002366 uhp = curbuf->b_u_curhead;
2367 if (uhp != NULL)
2368 uhp = uhp->uh_next.ptr;
2369 else
2370 uhp = curbuf->b_u_newhead;
2371 if (uhp != NULL && uhp->uh_save_nr != 0)
Bram Moolenaare38eab22019-12-05 21:50:01 +01002372 // "uh_save_nr" was set in the last block, that means
2373 // there were no changes since the last write
Bram Moolenaar730cde92010-06-27 05:18:54 +02002374 target = curbuf->b_u_save_nr_cur + step;
2375 else
Bram Moolenaare38eab22019-12-05 21:50:01 +01002376 // count the changes since the last write as one step
Bram Moolenaar730cde92010-06-27 05:18:54 +02002377 target = curbuf->b_u_save_nr_cur + step + 1;
2378 if (target <= 0)
Bram Moolenaare38eab22019-12-05 21:50:01 +01002379 // Go to before first write: before the oldest change. Use
2380 // the sequence number for that.
Bram Moolenaar730cde92010-06-27 05:18:54 +02002381 dofile = FALSE;
2382 }
2383 else
2384 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002385 // Moving forward to a newer write.
Bram Moolenaar730cde92010-06-27 05:18:54 +02002386 target = curbuf->b_u_save_nr_cur + step;
2387 if (target > curbuf->b_u_save_nr_last)
2388 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002389 // Go to after last write: after the latest change. Use
2390 // the sequence number for that.
Bram Moolenaar730cde92010-06-27 05:18:54 +02002391 target = curbuf->b_u_seq_last + 1;
2392 dofile = FALSE;
2393 }
2394 }
2395 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002396 else
2397 target = curbuf->b_u_seq_cur + step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002398 if (step < 0)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002399 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002400 if (target < 0)
2401 target = 0;
2402 closest = -1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002403 }
2404 else
2405 {
Bram Moolenaar730cde92010-06-27 05:18:54 +02002406 if (dosec)
Bram Moolenaarcbd4de42017-01-07 16:14:57 +01002407 closest = (long)(vim_time() + 1);
Bram Moolenaar730cde92010-06-27 05:18:54 +02002408 else if (dofile)
2409 closest = curbuf->b_u_save_nr_last + 2;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002410 else
2411 closest = curbuf->b_u_seq_last + 2;
2412 if (target >= closest)
2413 target = closest - 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002414 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002415 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002416 closest_start = closest;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002417 closest_seq = curbuf->b_u_seq_cur;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002418
Bram Moolenaare38eab22019-12-05 21:50:01 +01002419 // When "target" is 0; Back to origin.
Bram Moolenaarce46d932018-01-30 22:46:06 +01002420 if (target == 0)
Bram Moolenaar059fd012018-01-31 14:25:53 +01002421 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002422 mark = lastmark; // avoid that GCC complains
Bram Moolenaar059fd012018-01-31 14:25:53 +01002423 goto target_zero;
2424 }
Bram Moolenaarce46d932018-01-30 22:46:06 +01002425
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002426 /*
2427 * May do this twice:
Bram Moolenaar1e607892006-03-13 22:15:53 +00002428 * 1. Search for "target", update "closest" to the best match found.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002429 * 2. If "target" not found search for "closest".
2430 *
2431 * When using the closest time we use the sequence number in the second
2432 * round, because there may be several entries with the same time.
2433 */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002434 for (round = 1; round <= 2; ++round)
2435 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002436 // Find the path from the current state to where we want to go. The
2437 // desired state can be anywhere in the undo tree, need to go all over
2438 // it. We put "nomark" in uh_walk where we have been without success,
2439 // "mark" where it could possibly be.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002440 mark = ++lastmark;
2441 nomark = ++lastmark;
2442
Bram Moolenaare38eab22019-12-05 21:50:01 +01002443 if (curbuf->b_u_curhead == NULL) // at leaf of the tree
Bram Moolenaar1e607892006-03-13 22:15:53 +00002444 uhp = curbuf->b_u_newhead;
2445 else
2446 uhp = curbuf->b_u_curhead;
2447
2448 while (uhp != NULL)
2449 {
2450 uhp->uh_walk = mark;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002451 if (dosec)
Bram Moolenaarcbd4de42017-01-07 16:14:57 +01002452 val = (long)(uhp->uh_time);
Bram Moolenaar730cde92010-06-27 05:18:54 +02002453 else if (dofile)
2454 val = uhp->uh_save_nr;
2455 else
2456 val = uhp->uh_seq;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002457
Bram Moolenaar730cde92010-06-27 05:18:54 +02002458 if (round == 1 && !(dofile && val == 0))
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002459 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002460 // Remember the header that is closest to the target.
2461 // It must be at least in the right direction (checked with
2462 // "b_u_seq_cur"). When the timestamp is equal find the
2463 // highest/lowest sequence number.
Bram Moolenaarca003e12006-03-17 23:19:38 +00002464 if ((step < 0 ? uhp->uh_seq <= curbuf->b_u_seq_cur
2465 : uhp->uh_seq > curbuf->b_u_seq_cur)
2466 && ((dosec && val == closest)
2467 ? (step < 0
2468 ? uhp->uh_seq < closest_seq
2469 : uhp->uh_seq > closest_seq)
2470 : closest == closest_start
2471 || (val > target
2472 ? (closest > target
2473 ? val - target <= closest - target
2474 : val - target <= target - closest)
2475 : (closest > target
2476 ? target - val <= closest - target
2477 : target - val <= target - closest))))
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002478 {
2479 closest = val;
2480 closest_seq = uhp->uh_seq;
2481 }
2482 }
2483
Bram Moolenaare38eab22019-12-05 21:50:01 +01002484 // Quit searching when we found a match. But when searching for a
2485 // time we need to continue looking for the best uh_seq.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002486 if (target == val && !dosec)
Bram Moolenaar730cde92010-06-27 05:18:54 +02002487 {
2488 target = uhp->uh_seq;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002489 break;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002490 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002491
Bram Moolenaare38eab22019-12-05 21:50:01 +01002492 // go down in the tree if we haven't been there
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002493 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
2494 && uhp->uh_prev.ptr->uh_walk != mark)
2495 uhp = uhp->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002496
Bram Moolenaare38eab22019-12-05 21:50:01 +01002497 // go to alternate branch if we haven't been there
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002498 else if (uhp->uh_alt_next.ptr != NULL
2499 && uhp->uh_alt_next.ptr->uh_walk != nomark
2500 && uhp->uh_alt_next.ptr->uh_walk != mark)
2501 uhp = uhp->uh_alt_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002502
Bram Moolenaare38eab22019-12-05 21:50:01 +01002503 // go up in the tree if we haven't been there and we are at the
2504 // start of alternate branches
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002505 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
2506 && uhp->uh_next.ptr->uh_walk != nomark
2507 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002508 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002509 // If still at the start we don't go through this change.
Bram Moolenaardb552d602006-03-23 22:59:57 +00002510 if (uhp == curbuf->b_u_curhead)
2511 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002512 uhp = uhp->uh_next.ptr;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002513 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002514
2515 else
2516 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002517 // need to backtrack; mark this node as useless
Bram Moolenaar1e607892006-03-13 22:15:53 +00002518 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002519 if (uhp->uh_alt_prev.ptr != NULL)
2520 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002521 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002522 uhp = uhp->uh_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002523 }
2524 }
2525
Bram Moolenaare38eab22019-12-05 21:50:01 +01002526 if (uhp != NULL) // found it
Bram Moolenaar1e607892006-03-13 22:15:53 +00002527 break;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002528
2529 if (absolute)
2530 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00002531 semsg(_(e_undo_number_nr_not_found), step);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002532 return;
2533 }
2534
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002535 if (closest == closest_start)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002536 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002537 if (step < 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002538 msg(_("Already at oldest change"));
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002539 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002540 msg(_("Already at newest change"));
Bram Moolenaar1e607892006-03-13 22:15:53 +00002541 return;
2542 }
2543
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002544 target = closest_seq;
2545 dosec = FALSE;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002546 dofile = FALSE;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002547 if (step < 0)
Bram Moolenaare38eab22019-12-05 21:50:01 +01002548 above = TRUE; // stop above the header
Bram Moolenaar1e607892006-03-13 22:15:53 +00002549 }
2550
Bram Moolenaar059fd012018-01-31 14:25:53 +01002551target_zero:
Bram Moolenaare38eab22019-12-05 21:50:01 +01002552 // If we found it: Follow the path to go to where we want to be.
Bram Moolenaarce46d932018-01-30 22:46:06 +01002553 if (uhp != NULL || target == 0)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002554 {
2555 /*
2556 * First go up the tree as much as needed.
2557 */
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002558 while (!got_int)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002559 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002560 // Do the change warning now, for the same reason as above.
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002561 change_warning(0);
2562
Bram Moolenaar1e607892006-03-13 22:15:53 +00002563 uhp = curbuf->b_u_curhead;
2564 if (uhp == NULL)
2565 uhp = curbuf->b_u_newhead;
2566 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002567 uhp = uhp->uh_next.ptr;
Bram Moolenaarce46d932018-01-30 22:46:06 +01002568 if (uhp == NULL || (target > 0 && uhp->uh_walk != mark)
Bram Moolenaarca003e12006-03-17 23:19:38 +00002569 || (uhp->uh_seq == target && !above))
Bram Moolenaar1e607892006-03-13 22:15:53 +00002570 break;
2571 curbuf->b_u_curhead = uhp;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002572 u_undoredo(TRUE);
Bram Moolenaarce46d932018-01-30 22:46:06 +01002573 if (target > 0)
Bram Moolenaare38eab22019-12-05 21:50:01 +01002574 uhp->uh_walk = nomark; // don't go back down here
Bram Moolenaar1e607892006-03-13 22:15:53 +00002575 }
2576
Bram Moolenaare38eab22019-12-05 21:50:01 +01002577 // When back to origin, redo is not needed.
Bram Moolenaarce46d932018-01-30 22:46:06 +01002578 if (target > 0)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002579 {
Bram Moolenaarce46d932018-01-30 22:46:06 +01002580 /*
2581 * And now go down the tree (redo), branching off where needed.
2582 */
2583 while (!got_int)
2584 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002585 // Do the change warning now, for the same reason as above.
Bram Moolenaarce46d932018-01-30 22:46:06 +01002586 change_warning(0);
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002587
Bram Moolenaarce46d932018-01-30 22:46:06 +01002588 uhp = curbuf->b_u_curhead;
2589 if (uhp == NULL)
2590 break;
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002591
Bram Moolenaare38eab22019-12-05 21:50:01 +01002592 // Go back to the first branch with a mark.
Bram Moolenaarce46d932018-01-30 22:46:06 +01002593 while (uhp->uh_alt_prev.ptr != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002594 && uhp->uh_alt_prev.ptr->uh_walk == mark)
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002595 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaarce46d932018-01-30 22:46:06 +01002596
Bram Moolenaare38eab22019-12-05 21:50:01 +01002597 // Find the last branch with a mark, that's the one.
Bram Moolenaarce46d932018-01-30 22:46:06 +01002598 last = uhp;
2599 while (last->uh_alt_next.ptr != NULL
2600 && last->uh_alt_next.ptr->uh_walk == mark)
2601 last = last->uh_alt_next.ptr;
2602 if (last != uhp)
2603 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002604 // Make the used branch the first entry in the list of
2605 // alternatives to make "u" and CTRL-R take this branch.
Bram Moolenaarce46d932018-01-30 22:46:06 +01002606 while (uhp->uh_alt_prev.ptr != NULL)
2607 uhp = uhp->uh_alt_prev.ptr;
2608 if (last->uh_alt_next.ptr != NULL)
2609 last->uh_alt_next.ptr->uh_alt_prev.ptr =
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002610 last->uh_alt_prev.ptr;
Bram Moolenaarce46d932018-01-30 22:46:06 +01002611 last->uh_alt_prev.ptr->uh_alt_next.ptr =
2612 last->uh_alt_next.ptr;
2613 last->uh_alt_prev.ptr = NULL;
2614 last->uh_alt_next.ptr = uhp;
2615 uhp->uh_alt_prev.ptr = last;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002616
Bram Moolenaarce46d932018-01-30 22:46:06 +01002617 if (curbuf->b_u_oldhead == uhp)
2618 curbuf->b_u_oldhead = last;
2619 uhp = last;
2620 if (uhp->uh_next.ptr != NULL)
2621 uhp->uh_next.ptr->uh_prev.ptr = uhp;
2622 }
2623 curbuf->b_u_curhead = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002624
Bram Moolenaarce46d932018-01-30 22:46:06 +01002625 if (uhp->uh_walk != mark)
Bram Moolenaare38eab22019-12-05 21:50:01 +01002626 break; // must have reached the target
Bram Moolenaar1e607892006-03-13 22:15:53 +00002627
Bram Moolenaare38eab22019-12-05 21:50:01 +01002628 // Stop when going backwards in time and didn't find the exact
2629 // header we were looking for.
Bram Moolenaarce46d932018-01-30 22:46:06 +01002630 if (uhp->uh_seq == target && above)
2631 {
2632 curbuf->b_u_seq_cur = target - 1;
2633 break;
2634 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002635
Bram Moolenaarce46d932018-01-30 22:46:06 +01002636 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002637
Bram Moolenaare38eab22019-12-05 21:50:01 +01002638 // Advance "curhead" to below the header we last used. If it
2639 // becomes NULL then we need to set "newhead" to this leaf.
Bram Moolenaarce46d932018-01-30 22:46:06 +01002640 if (uhp->uh_prev.ptr == NULL)
2641 curbuf->b_u_newhead = uhp;
2642 curbuf->b_u_curhead = uhp->uh_prev.ptr;
2643 did_undo = FALSE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002644
Bram Moolenaare38eab22019-12-05 21:50:01 +01002645 if (uhp->uh_seq == target) // found it!
Bram Moolenaarce46d932018-01-30 22:46:06 +01002646 break;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002647
Bram Moolenaarce46d932018-01-30 22:46:06 +01002648 uhp = uhp->uh_prev.ptr;
2649 if (uhp == NULL || uhp->uh_walk != mark)
2650 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002651 // Need to redo more but can't find it...
Bram Moolenaarce46d932018-01-30 22:46:06 +01002652 internal_error("undo_time()");
2653 break;
2654 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002655 }
2656 }
2657 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00002658 u_undo_end(did_undo, absolute);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002659}
2660
2661/*
2662 * u_undoredo: common code for undo and redo
2663 *
2664 * The lines in the file are replaced by the lines in the entry list at
2665 * curbuf->b_u_curhead. The replaced lines in the file are saved in the entry
2666 * list for the next undo/redo.
Bram Moolenaarca003e12006-03-17 23:19:38 +00002667 *
2668 * When "undo" is TRUE we go up in the tree, when FALSE we go down.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002669 */
2670 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002671u_undoredo(int undo)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002672{
Bram Moolenaarccae4672019-01-04 15:09:57 +01002673 undoline_T *newarray = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002674 linenr_T oldsize;
2675 linenr_T newsize;
2676 linenr_T top, bot;
2677 linenr_T lnum;
2678 linenr_T newlnum = MAXLNUM;
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002679 pos_T new_curpos = curwin->w_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002680 long i;
2681 u_entry_T *uep, *nuep;
2682 u_entry_T *newlist = NULL;
2683 int old_flags;
2684 int new_flags;
2685 pos_T namedm[NMARKS];
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002686 visualinfo_T visualinfo;
Bram Moolenaare38eab22019-12-05 21:50:01 +01002687 int empty_buffer; // buffer became empty
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002688 u_header_T *curhead = curbuf->b_u_curhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002689
Bram Moolenaare38eab22019-12-05 21:50:01 +01002690 // Don't want autocommands using the undo structures here, they are
2691 // invalid till the end.
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002692 block_autocmds();
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002693
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002694#ifdef U_DEBUG
2695 u_check(FALSE);
2696#endif
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002697 old_flags = curhead->uh_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002698 new_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
2699 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
2700 setpcmark();
2701
2702 /*
2703 * save marks before undo/redo
2704 */
2705 mch_memmove(namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002706 visualinfo = curbuf->b_visual;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002707 curbuf->b_op_start.lnum = curbuf->b_ml.ml_line_count;
2708 curbuf->b_op_start.col = 0;
2709 curbuf->b_op_end.lnum = 0;
2710 curbuf->b_op_end.col = 0;
2711
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002712 for (uep = curhead->uh_entry; uep != NULL; uep = nuep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002713 {
2714 top = uep->ue_top;
2715 bot = uep->ue_bot;
2716 if (bot == 0)
2717 bot = curbuf->b_ml.ml_line_count + 1;
2718 if (top > curbuf->b_ml.ml_line_count || top >= bot
2719 || bot > curbuf->b_ml.ml_line_count + 1)
2720 {
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002721 unblock_autocmds();
RestorerZ68ebcee2023-05-31 17:12:14 +01002722 iemsg(e_u_undo_line_numbers_wrong);
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002723 changed(); // don't want UNCHANGED now
Bram Moolenaar071d4272004-06-13 20:20:40 +00002724 return;
2725 }
2726
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002727 oldsize = bot - top - 1; // number of lines before undo
2728 newsize = uep->ue_size; // number of lines after undo
Bram Moolenaar071d4272004-06-13 20:20:40 +00002729
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002730 // Decide about the cursor position, depending on what text changed.
2731 // Don't set it yet, it may be invalid if lines are going to be added.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002732 if (top < newlnum)
2733 {
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002734 // If the saved cursor is somewhere in this undo block, move it to
2735 // the remembered position. Makes "gwap" put the cursor back
2736 // where it was.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002737 lnum = curhead->uh_cursor.lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002738 if (lnum >= top && lnum <= top + newsize + 1)
2739 {
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002740 new_curpos = curhead->uh_cursor;
2741 newlnum = new_curpos.lnum - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002742 }
2743 else
2744 {
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002745 // Use the first line that actually changed. Avoids that
2746 // undoing auto-formatting puts the cursor in the previous
2747 // line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002748 for (i = 0; i < newsize && i < oldsize; ++i)
Bram Moolenaarccae4672019-01-04 15:09:57 +01002749 {
2750 char_u *p = ml_get(top + 1 + i);
2751
2752 if (curbuf->b_ml.ml_line_len != uep->ue_array[i].ul_len
Bram Moolenaar964b3742019-05-24 18:54:09 +02002753 || memcmp(uep->ue_array[i].ul_line, p,
2754 curbuf->b_ml.ml_line_len) != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002755 break;
Bram Moolenaarccae4672019-01-04 15:09:57 +01002756 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002757 if (i == newsize && newlnum == MAXLNUM && uep->ue_next == NULL)
2758 {
2759 newlnum = top;
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002760 new_curpos.lnum = newlnum + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002761 }
2762 else if (i < newsize)
2763 {
2764 newlnum = top + i;
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002765 new_curpos.lnum = newlnum + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002766 }
2767 }
2768 }
2769
2770 empty_buffer = FALSE;
2771
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002772 /*
2773 * Delete the lines between top and bot and save them in newarray.
2774 */
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002775 if (oldsize > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002776 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02002777 if ((newarray = U_ALLOC_LINE(sizeof(undoline_T) * oldsize)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002778 {
Bram Moolenaarccae4672019-01-04 15:09:57 +01002779 do_outofmem_msg((long_u)(sizeof(undoline_T) * oldsize));
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002780
2781 // We have messed up the entry list, repair is impossible.
2782 // we have to free the rest of the list.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002783 while (uep != NULL)
2784 {
2785 nuep = uep->ue_next;
2786 u_freeentry(uep, uep->ue_size);
2787 uep = nuep;
2788 }
2789 break;
2790 }
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002791 // delete backwards, it goes faster in most cases
Bram Moolenaar071d4272004-06-13 20:20:40 +00002792 for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum)
2793 {
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002794 // what can we do when we run out of memory?
Bram Moolenaarccae4672019-01-04 15:09:57 +01002795 if (u_save_line(&newarray[i], lnum) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002796 do_outofmem_msg((long_u)0);
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002797 // remember we deleted the last line in the buffer, and a
2798 // dummy empty line will be inserted
Bram Moolenaar071d4272004-06-13 20:20:40 +00002799 if (curbuf->b_ml.ml_line_count == 1)
2800 empty_buffer = TRUE;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002801 ml_delete_flags(lnum, ML_DEL_UNDO);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002802 }
2803 }
Bram Moolenaar8d343302005-07-12 22:46:17 +00002804 else
2805 newarray = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002806
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002807 // make sure the cursor is on a valid line after the deletions
2808 check_cursor_lnum();
2809
2810 /*
2811 * Insert the lines in u_array between top and bot.
2812 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002813 if (newsize)
2814 {
2815 for (lnum = top, i = 0; i < newsize; ++i, ++lnum)
2816 {
Bram Moolenaarccae4672019-01-04 15:09:57 +01002817 // If the file is empty, there is an empty line 1 that we
2818 // should get rid of, by replacing it with the new line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002819 if (empty_buffer && lnum == 0)
Bram Moolenaar964b3742019-05-24 18:54:09 +02002820 ml_replace_len((linenr_T)1, uep->ue_array[i].ul_line,
2821 uep->ue_array[i].ul_len, TRUE, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002822 else
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002823 ml_append_flags(lnum, uep->ue_array[i].ul_line,
2824 (colnr_T)uep->ue_array[i].ul_len, ML_APPEND_UNDO);
Bram Moolenaarccae4672019-01-04 15:09:57 +01002825 vim_free(uep->ue_array[i].ul_line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002826 }
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002827 vim_free((char_u *)uep->ue_array);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002828 }
2829
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002830 // adjust marks
Bram Moolenaar071d4272004-06-13 20:20:40 +00002831 if (oldsize != newsize)
2832 {
2833 mark_adjust(top + 1, top + oldsize, (long)MAXLNUM,
2834 (long)newsize - (long)oldsize);
2835 if (curbuf->b_op_start.lnum > top + oldsize)
2836 curbuf->b_op_start.lnum += newsize - oldsize;
2837 if (curbuf->b_op_end.lnum > top + oldsize)
2838 curbuf->b_op_end.lnum += newsize - oldsize;
2839 }
Bram Moolenaar55737c22022-02-14 14:51:22 +00002840 if (oldsize > 0 || newsize > 0)
Bram Moolenaar26f09ea2022-09-27 16:29:38 +01002841 {
Bram Moolenaar55737c22022-02-14 14:51:22 +00002842 changed_lines(top + 1, 0, bot, newsize - oldsize);
Bram Moolenaar26f09ea2022-09-27 16:29:38 +01002843#ifdef FEAT_SPELL
2844 // When text has been changed, possibly the start of the next line
2845 // may have SpellCap that should be removed or it needs to be
2846 // displayed. Schedule the next line for redrawing just in case.
2847 if (spell_check_window(curwin) && bot <= curbuf->b_ml.ml_line_count)
2848 redrawWinline(curwin, bot);
2849#endif
2850 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002851
LemonBoy82444ce2022-05-12 15:39:31 +01002852 // Set the '[ mark.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002853 if (top + 1 < curbuf->b_op_start.lnum)
2854 curbuf->b_op_start.lnum = top + 1;
LemonBoy82444ce2022-05-12 15:39:31 +01002855 // Set the '] mark.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002856 if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum)
2857 curbuf->b_op_end.lnum = top + 1;
2858 else if (top + newsize > curbuf->b_op_end.lnum)
2859 curbuf->b_op_end.lnum = top + newsize;
2860
2861 u_newcount += newsize;
2862 u_oldcount += oldsize;
2863 uep->ue_size = oldsize;
2864 uep->ue_array = newarray;
2865 uep->ue_bot = top + newsize + 1;
2866
2867 /*
2868 * insert this entry in front of the new entry list
2869 */
2870 nuep = uep->ue_next;
2871 uep->ue_next = newlist;
2872 newlist = uep;
2873 }
2874
LemonBoy82444ce2022-05-12 15:39:31 +01002875 // Ensure the '[ and '] marks are within bounds.
2876 if (curbuf->b_op_start.lnum > curbuf->b_ml.ml_line_count)
2877 curbuf->b_op_start.lnum = curbuf->b_ml.ml_line_count;
2878 if (curbuf->b_op_end.lnum > curbuf->b_ml.ml_line_count)
2879 curbuf->b_op_end.lnum = curbuf->b_ml.ml_line_count;
2880
Bram Moolenaar4544bd22019-09-08 15:27:21 +02002881 // Set the cursor to the desired position. Check that the line is valid.
2882 curwin->w_cursor = new_curpos;
2883 check_cursor_lnum();
2884
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002885 curhead->uh_entry = newlist;
2886 curhead->uh_flags = new_flags;
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01002887 if ((old_flags & UH_EMPTYBUF) && BUFEMPTY())
Bram Moolenaar071d4272004-06-13 20:20:40 +00002888 curbuf->b_ml.ml_flags |= ML_EMPTY;
2889 if (old_flags & UH_CHANGED)
2890 changed();
2891 else
Bram Moolenaar009b2592004-10-24 19:18:58 +00002892#ifdef FEAT_NETBEANS_INTG
Bram Moolenaare38eab22019-12-05 21:50:01 +01002893 // per netbeans undo rules, keep it as modified
Bram Moolenaar009b2592004-10-24 19:18:58 +00002894 if (!isNetbeansModified(curbuf))
2895#endif
Bram Moolenaarc024b462019-06-08 18:07:21 +02002896 unchanged(curbuf, FALSE, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002897
2898 /*
2899 * restore marks from before undo/redo
2900 */
2901 for (i = 0; i < NMARKS; ++i)
Bram Moolenaarf65aad52015-02-17 13:43:40 +01002902 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002903 if (curhead->uh_namedm[i].lnum != 0)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002904 curbuf->b_namedm[i] = curhead->uh_namedm[i];
Bram Moolenaarf65aad52015-02-17 13:43:40 +01002905 if (namedm[i].lnum != 0)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002906 curhead->uh_namedm[i] = namedm[i];
Bram Moolenaarf65aad52015-02-17 13:43:40 +01002907 else
2908 curhead->uh_namedm[i].lnum = 0;
2909 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002910 if (curhead->uh_visual.vi_start.lnum != 0)
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002911 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002912 curbuf->b_visual = curhead->uh_visual;
2913 curhead->uh_visual = visualinfo;
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002914 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002915
2916 /*
2917 * If the cursor is only off by one line, put it at the same position as
2918 * before starting the change (for the "o" command).
2919 * Otherwise the cursor should go to the first undone line.
2920 */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002921 if (curhead->uh_cursor.lnum + 1 == curwin->w_cursor.lnum
Bram Moolenaar071d4272004-06-13 20:20:40 +00002922 && curwin->w_cursor.lnum > 1)
2923 --curwin->w_cursor.lnum;
Bram Moolenaar0390ded2010-08-07 12:54:12 +02002924 if (curwin->w_cursor.lnum <= curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002925 {
Bram Moolenaar0390ded2010-08-07 12:54:12 +02002926 if (curhead->uh_cursor.lnum == curwin->w_cursor.lnum)
2927 {
2928 curwin->w_cursor.col = curhead->uh_cursor.col;
Bram Moolenaar0390ded2010-08-07 12:54:12 +02002929 if (virtual_active() && curhead->uh_cursor_vcol >= 0)
2930 coladvance((colnr_T)curhead->uh_cursor_vcol);
2931 else
2932 curwin->w_cursor.coladd = 0;
Bram Moolenaar0390ded2010-08-07 12:54:12 +02002933 }
2934 else
2935 beginline(BL_SOL | BL_FIX);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002936 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002937 else
2938 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002939 // We get here with the current cursor line being past the end (eg
2940 // after adding lines at the end of the file, and then undoing it).
2941 // check_cursor() will move the cursor to the last line. Move it to
2942 // the first column here.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002943 curwin->w_cursor.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002944 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002945 }
2946
Bram Moolenaare38eab22019-12-05 21:50:01 +01002947 // Make sure the cursor is on an existing line and column.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002948 check_cursor();
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002949
Bram Moolenaare38eab22019-12-05 21:50:01 +01002950 // Remember where we are for "g-" and ":earlier 10s".
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002951 curbuf->b_u_seq_cur = curhead->uh_seq;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002952 if (undo)
Bram Moolenaar80eaddd2017-11-11 23:37:08 +01002953 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01002954 // We are below the previous undo. However, to make ":earlier 1s"
2955 // work we compute this as being just above the just undone change.
Bram Moolenaar80eaddd2017-11-11 23:37:08 +01002956 if (curhead->uh_next.ptr != NULL)
2957 curbuf->b_u_seq_cur = curhead->uh_next.ptr->uh_seq;
2958 else
2959 curbuf->b_u_seq_cur = 0;
2960 }
Bram Moolenaarca003e12006-03-17 23:19:38 +00002961
Bram Moolenaare38eab22019-12-05 21:50:01 +01002962 // Remember where we are for ":earlier 1f" and ":later 1f".
Bram Moolenaar730cde92010-06-27 05:18:54 +02002963 if (curhead->uh_save_nr != 0)
2964 {
2965 if (undo)
2966 curbuf->b_u_save_nr_cur = curhead->uh_save_nr - 1;
2967 else
2968 curbuf->b_u_save_nr_cur = curhead->uh_save_nr;
2969 }
2970
Bram Moolenaare38eab22019-12-05 21:50:01 +01002971 // The timestamp can be the same for multiple changes, just use the one of
2972 // the undone/redone change.
Bram Moolenaara800b422010-06-27 01:15:55 +02002973 curbuf->b_u_time_cur = curhead->uh_time;
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002974
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002975 unblock_autocmds();
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002976#ifdef U_DEBUG
2977 u_check(FALSE);
2978#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002979}
2980
2981/*
2982 * If we deleted or added lines, report the number of less/more lines.
2983 * Otherwise, report the number of changes (this may be incorrect
2984 * in some cases, but it's better than nothing).
2985 */
2986 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002987u_undo_end(
Bram Moolenaare38eab22019-12-05 21:50:01 +01002988 int did_undo, // just did an undo
2989 int absolute) // used ":undo N"
Bram Moolenaar071d4272004-06-13 20:20:40 +00002990{
Bram Moolenaar89d40322006-08-29 15:30:07 +00002991 char *msgstr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002992 u_header_T *uhp;
2993 char_u msgbuf[80];
Bram Moolenaar1e607892006-03-13 22:15:53 +00002994
Bram Moolenaar071d4272004-06-13 20:20:40 +00002995#ifdef FEAT_FOLDING
2996 if ((fdo_flags & FDO_UNDO) && KeyTyped)
2997 foldOpenCursor();
2998#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +00002999
Bram Moolenaare38eab22019-12-05 21:50:01 +01003000 if (global_busy // no messages now, wait until global is finished
3001 || !messaging()) // 'lazyredraw' set, don't do messages now
Bram Moolenaar1e607892006-03-13 22:15:53 +00003002 return;
3003
3004 if (curbuf->b_ml.ml_flags & ML_EMPTY)
3005 --u_newcount;
3006
3007 u_oldcount -= u_newcount;
3008 if (u_oldcount == -1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00003009 msgstr = N_("more line");
Bram Moolenaar1e607892006-03-13 22:15:53 +00003010 else if (u_oldcount < 0)
Bram Moolenaar89d40322006-08-29 15:30:07 +00003011 msgstr = N_("more lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00003012 else if (u_oldcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00003013 msgstr = N_("line less");
Bram Moolenaar1e607892006-03-13 22:15:53 +00003014 else if (u_oldcount > 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00003015 msgstr = N_("fewer lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00003016 else
3017 {
3018 u_oldcount = u_newcount;
3019 if (u_newcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00003020 msgstr = N_("change");
Bram Moolenaar1e607892006-03-13 22:15:53 +00003021 else
Bram Moolenaar89d40322006-08-29 15:30:07 +00003022 msgstr = N_("changes");
Bram Moolenaar1e607892006-03-13 22:15:53 +00003023 }
3024
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003025 if (curbuf->b_u_curhead != NULL)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00003026 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003027 // For ":undo N" we prefer a "after #N" message.
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003028 if (absolute && curbuf->b_u_curhead->uh_next.ptr != NULL)
Bram Moolenaardb552d602006-03-23 22:59:57 +00003029 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003030 uhp = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaardb552d602006-03-23 22:59:57 +00003031 did_undo = FALSE;
3032 }
3033 else if (did_undo)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00003034 uhp = curbuf->b_u_curhead;
3035 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003036 uhp = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00003037 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00003038 else
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003039 uhp = curbuf->b_u_newhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +00003040
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003041 if (uhp == NULL)
3042 *msgbuf = NUL;
3043 else
Bram Moolenaar52410572019-10-27 05:12:45 +01003044 add_time(msgbuf, sizeof(msgbuf), uhp->uh_time);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003045
Bram Moolenaarb2c03502010-07-02 20:20:09 +02003046#ifdef FEAT_CONCEAL
3047 {
3048 win_T *wp;
3049
3050 FOR_ALL_WINDOWS(wp)
3051 {
Bram Moolenaarf5963f72010-07-23 22:10:27 +02003052 if (wp->w_buffer == curbuf && wp->w_p_cole > 0)
Bram Moolenaara4d158b2022-08-14 14:17:45 +01003053 redraw_win_later(wp, UPD_NOT_VALID);
Bram Moolenaarb2c03502010-07-02 20:20:09 +02003054 }
3055 }
3056#endif
Bram Moolenaar8d02ce12022-01-25 18:24:00 +00003057 if (VIsual_active)
3058 check_pos(curbuf, &VIsual);
Bram Moolenaarb2c03502010-07-02 20:20:09 +02003059
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003060 smsg_attr_keep(0, _("%ld %s; %s #%ld %s"),
Bram Moolenaarca003e12006-03-17 23:19:38 +00003061 u_oldcount < 0 ? -u_oldcount : u_oldcount,
Bram Moolenaar89d40322006-08-29 15:30:07 +00003062 _(msgstr),
Bram Moolenaar433f7c82006-03-21 21:29:36 +00003063 did_undo ? _("before") : _("after"),
3064 uhp == NULL ? 0L : uhp->uh_seq,
3065 msgbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003066}
3067
3068/*
3069 * u_sync: stop adding to the current entry list
3070 */
3071 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003072u_sync(
Bram Moolenaare38eab22019-12-05 21:50:01 +01003073 int force) // Also sync when no_u_sync is set.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003074{
Bram Moolenaare38eab22019-12-05 21:50:01 +01003075 // Skip it when already synced or syncing is disabled.
Bram Moolenaar779b74b2006-04-10 14:55:34 +00003076 if (curbuf->b_u_synced || (!force && no_u_sync > 0))
3077 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003078#if defined(FEAT_XIM) && defined(FEAT_GUI_GTK)
Bram Moolenaar5c6dbcb2017-08-30 22:00:20 +02003079 if (p_imst == IM_ON_THE_SPOT && im_is_preediting())
Bram Moolenaare38eab22019-12-05 21:50:01 +01003080 return; // XIM is busy, don't break an undo sequence
Bram Moolenaar071d4272004-06-13 20:20:40 +00003081#endif
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +01003082 if (get_undolevel() < 0)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003083 curbuf->b_u_synced = TRUE; // no entries, nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00003084 else
3085 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003086 u_getbot(); // compute ue_bot of previous u_save
Bram Moolenaar071d4272004-06-13 20:20:40 +00003087 curbuf->b_u_curhead = NULL;
3088 }
3089}
3090
3091/*
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003092 * ":undolist": List the leafs of the undo tree
3093 */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003094 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003095ex_undolist(exarg_T *eap UNUSED)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003096{
3097 garray_T ga;
3098 u_header_T *uhp;
3099 int mark;
3100 int nomark;
3101 int changes = 1;
John Marriottfd1a8382024-11-06 21:21:50 +01003102 int len;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003103
3104 /*
3105 * 1: walk the tree to find all leafs, put the info in "ga".
3106 * 2: sort the lines
3107 * 3: display the list
3108 */
3109 mark = ++lastmark;
3110 nomark = ++lastmark;
Bram Moolenaar04935fb2022-01-08 16:19:22 +00003111 ga_init2(&ga, sizeof(char *), 20);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003112
3113 uhp = curbuf->b_u_oldhead;
3114 while (uhp != NULL)
3115 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003116 if (uhp->uh_prev.ptr == NULL && uhp->uh_walk != nomark
Bram Moolenaarca003e12006-03-17 23:19:38 +00003117 && uhp->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003118 {
3119 if (ga_grow(&ga, 1) == FAIL)
3120 break;
John Marriottfd1a8382024-11-06 21:21:50 +01003121 len = vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7d ",
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003122 uhp->uh_seq, changes);
John Marriottfd1a8382024-11-06 21:21:50 +01003123 add_time(IObuff + len, IOSIZE - len, uhp->uh_time);
3124
3125 // we have to call STRLEN() here because add_time() does not report
3126 // the number of characters added.
Yegappan Lakshmanan084529c2024-12-24 09:50:01 +01003127 len += (int)STRLEN(IObuff + len);
Bram Moolenaara800b422010-06-27 01:15:55 +02003128 if (uhp->uh_save_nr > 0)
3129 {
John Marriottfd1a8382024-11-06 21:21:50 +01003130 int n = (len >= 33) ? 0 : 33 - len;
3131
3132 len += vim_snprintf((char *)IObuff + len, IOSIZE - len, "%*.*s %3ld", n, n, " ", uhp->uh_save_nr);
Bram Moolenaara800b422010-06-27 01:15:55 +02003133 }
John Marriottfd1a8382024-11-06 21:21:50 +01003134 ((char_u **)(ga.ga_data))[ga.ga_len++] = vim_strnsave(IObuff, len);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003135 }
3136
3137 uhp->uh_walk = mark;
3138
Bram Moolenaare38eab22019-12-05 21:50:01 +01003139 // go down in the tree if we haven't been there
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003140 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
3141 && uhp->uh_prev.ptr->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003142 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003143 uhp = uhp->uh_prev.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003144 ++changes;
3145 }
3146
Bram Moolenaare38eab22019-12-05 21:50:01 +01003147 // go to alternate branch if we haven't been there
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003148 else if (uhp->uh_alt_next.ptr != NULL
3149 && uhp->uh_alt_next.ptr->uh_walk != nomark
3150 && uhp->uh_alt_next.ptr->uh_walk != mark)
3151 uhp = uhp->uh_alt_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003152
Bram Moolenaare38eab22019-12-05 21:50:01 +01003153 // go up in the tree if we haven't been there and we are at the
3154 // start of alternate branches
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003155 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
3156 && uhp->uh_next.ptr->uh_walk != nomark
3157 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003158 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003159 uhp = uhp->uh_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003160 --changes;
3161 }
3162
3163 else
3164 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003165 // need to backtrack; mark this node as done
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003166 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003167 if (uhp->uh_alt_prev.ptr != NULL)
3168 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003169 else
3170 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003171 uhp = uhp->uh_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003172 --changes;
3173 }
3174 }
3175 }
3176
3177 if (ga.ga_len == 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01003178 msg(_("Nothing to undo"));
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003179 else
3180 {
John Marriottfd1a8382024-11-06 21:21:50 +01003181 int i;
3182
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003183 sort_strings((char_u **)ga.ga_data, ga.ga_len);
3184
3185 msg_start();
Bram Moolenaar32526b32019-01-19 17:43:09 +01003186 msg_puts_attr(_("number changes when saved"),
Bram Moolenaar8820b482017-03-16 17:23:31 +01003187 HL_ATTR(HLF_T));
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003188 for (i = 0; i < ga.ga_len && !got_int; ++i)
3189 {
3190 msg_putchar('\n');
3191 if (got_int)
3192 break;
Bram Moolenaar32526b32019-01-19 17:43:09 +01003193 msg_puts(((char **)ga.ga_data)[i]);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003194 }
3195 msg_end();
3196
3197 ga_clear_strings(&ga);
3198 }
3199}
3200
3201/*
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003202 * ":undojoin": continue adding to the last entry list
3203 */
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003204 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003205ex_undojoin(exarg_T *eap UNUSED)
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003206{
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003207 if (curbuf->b_u_newhead == NULL)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003208 return; // nothing changed before
Bram Moolenaar57657d82006-04-21 22:12:41 +00003209 if (curbuf->b_u_curhead != NULL)
3210 {
Bram Moolenaar677658a2022-01-05 16:09:06 +00003211 emsg(_(e_undojoin_is_not_allowed_after_undo));
Bram Moolenaar57657d82006-04-21 22:12:41 +00003212 return;
3213 }
3214 if (!curbuf->b_u_synced)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003215 return; // already unsynced
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +01003216 if (get_undolevel() < 0)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003217 return; // no entries, nothing to do
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003218 else
Bram Moolenaare38eab22019-12-05 21:50:01 +01003219 // Append next change to the last entry
Bram Moolenaar5e4e1b12017-01-17 22:09:45 +01003220 curbuf->b_u_synced = FALSE;
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003221}
3222
3223/*
Bram Moolenaar59f931e2010-07-24 20:27:03 +02003224 * Called after writing or reloading the file and setting b_changed to FALSE.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003225 * Now an undo means that the buffer is modified.
3226 */
3227 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003228u_unchanged(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003229{
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003230 u_unch_branch(buf->b_u_oldhead);
3231 buf->b_did_warn = FALSE;
3232}
3233
Bram Moolenaar730cde92010-06-27 05:18:54 +02003234/*
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003235 * After reloading a buffer which was saved for 'undoreload': Find the first
3236 * line that was changed and set the cursor there.
3237 */
3238 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003239u_find_first_changed(void)
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003240{
3241 u_header_T *uhp = curbuf->b_u_newhead;
3242 u_entry_T *uep;
3243 linenr_T lnum;
3244
3245 if (curbuf->b_u_curhead != NULL || uhp == NULL)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003246 return; // undid something in an autocmd?
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003247
Bram Moolenaare38eab22019-12-05 21:50:01 +01003248 // Check that the last undo block was for the whole file.
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003249 uep = uhp->uh_entry;
3250 if (uep->ue_top != 0 || uep->ue_bot != 0)
3251 return;
3252
3253 for (lnum = 1; lnum < curbuf->b_ml.ml_line_count
3254 && lnum <= uep->ue_size; ++lnum)
Bram Moolenaarccae4672019-01-04 15:09:57 +01003255 {
3256 char_u *p = ml_get_buf(curbuf, lnum, FALSE);
3257
3258 if (uep->ue_array[lnum - 1].ul_len != curbuf->b_ml.ml_line_len
3259 || memcmp(p, uep->ue_array[lnum - 1].ul_line, uep->ue_array[lnum - 1].ul_len) != 0)
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003260 {
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01003261 CLEAR_POS(&(uhp->uh_cursor));
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003262 uhp->uh_cursor.lnum = lnum;
3263 return;
3264 }
Bram Moolenaarccae4672019-01-04 15:09:57 +01003265 }
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003266 if (curbuf->b_ml.ml_line_count != uep->ue_size)
3267 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003268 // lines added or deleted at the end, put the cursor there
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01003269 CLEAR_POS(&(uhp->uh_cursor));
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003270 uhp->uh_cursor.lnum = lnum;
3271 }
3272}
3273
3274/*
Bram Moolenaar730cde92010-06-27 05:18:54 +02003275 * Increase the write count, store it in the last undo header, what would be
3276 * used for "u".
3277 */
3278 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003279u_update_save_nr(buf_T *buf)
Bram Moolenaar730cde92010-06-27 05:18:54 +02003280{
3281 u_header_T *uhp;
3282
3283 ++buf->b_u_save_nr_last;
3284 buf->b_u_save_nr_cur = buf->b_u_save_nr_last;
3285 uhp = buf->b_u_curhead;
3286 if (uhp != NULL)
3287 uhp = uhp->uh_next.ptr;
3288 else
3289 uhp = buf->b_u_newhead;
3290 if (uhp != NULL)
3291 uhp->uh_save_nr = buf->b_u_save_nr_last;
3292}
3293
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003294 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003295u_unch_branch(u_header_T *uhp)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003296{
Bram Moolenaar1e607892006-03-13 22:15:53 +00003297 u_header_T *uh;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003298
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003299 for (uh = uhp; uh != NULL; uh = uh->uh_prev.ptr)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003300 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003301 uh->uh_flags |= UH_CHANGED;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003302 if (uh->uh_alt_next.ptr != NULL)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003303 u_unch_branch(uh->uh_alt_next.ptr); // recursive
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003304 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003305}
3306
3307/*
3308 * Get pointer to last added entry.
3309 * If it's not valid, give an error message and return NULL.
3310 */
3311 static u_entry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003312u_get_headentry(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003313{
3314 if (curbuf->b_u_newhead == NULL || curbuf->b_u_newhead->uh_entry == NULL)
3315 {
RestorerZ68ebcee2023-05-31 17:12:14 +01003316 iemsg(e_undo_list_corrupt);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003317 return NULL;
3318 }
3319 return curbuf->b_u_newhead->uh_entry;
3320}
3321
3322/*
3323 * u_getbot(): compute the line number of the previous u_save
3324 * It is called only when b_u_synced is FALSE.
3325 */
3326 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003327u_getbot(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003328{
3329 u_entry_T *uep;
3330 linenr_T extra;
3331
Bram Moolenaare38eab22019-12-05 21:50:01 +01003332 uep = u_get_headentry(); // check for corrupt undo list
Bram Moolenaar071d4272004-06-13 20:20:40 +00003333 if (uep == NULL)
3334 return;
3335
3336 uep = curbuf->b_u_newhead->uh_getbot_entry;
3337 if (uep != NULL)
3338 {
3339 /*
3340 * the new ue_bot is computed from the number of lines that has been
3341 * inserted (0 - deleted) since calling u_save. This is equal to the
3342 * old line count subtracted from the current line count.
3343 */
3344 extra = curbuf->b_ml.ml_line_count - uep->ue_lcount;
3345 uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra;
3346 if (uep->ue_bot < 1 || uep->ue_bot > curbuf->b_ml.ml_line_count)
3347 {
RestorerZ68ebcee2023-05-31 17:12:14 +01003348 iemsg(e_undo_line_missing);
Bram Moolenaare38eab22019-12-05 21:50:01 +01003349 uep->ue_bot = uep->ue_top + 1; // assume all lines deleted, will
3350 // get all the old lines back
3351 // without deleting the current
3352 // ones
Bram Moolenaar071d4272004-06-13 20:20:40 +00003353 }
3354
3355 curbuf->b_u_newhead->uh_getbot_entry = NULL;
3356 }
3357
3358 curbuf->b_u_synced = TRUE;
3359}
3360
3361/*
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003362 * Free one header "uhp" and its entry list and adjust the pointers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003363 */
3364 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003365u_freeheader(
3366 buf_T *buf,
3367 u_header_T *uhp,
Bram Moolenaare38eab22019-12-05 21:50:01 +01003368 u_header_T **uhpp) // if not NULL reset when freeing this header
Bram Moolenaar071d4272004-06-13 20:20:40 +00003369{
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003370 u_header_T *uhap;
3371
Bram Moolenaare38eab22019-12-05 21:50:01 +01003372 // When there is an alternate redo list free that branch completely,
3373 // because we can never go there.
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003374 if (uhp->uh_alt_next.ptr != NULL)
3375 u_freebranch(buf, uhp->uh_alt_next.ptr, uhpp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003376
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003377 if (uhp->uh_alt_prev.ptr != NULL)
3378 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003379
Bram Moolenaare38eab22019-12-05 21:50:01 +01003380 // Update the links in the list to remove the header.
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003381 if (uhp->uh_next.ptr == NULL)
3382 buf->b_u_oldhead = uhp->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003383 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003384 uhp->uh_next.ptr->uh_prev.ptr = uhp->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003385
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003386 if (uhp->uh_prev.ptr == NULL)
3387 buf->b_u_newhead = uhp->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003388 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003389 for (uhap = uhp->uh_prev.ptr; uhap != NULL;
3390 uhap = uhap->uh_alt_next.ptr)
3391 uhap->uh_next.ptr = uhp->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003392
Bram Moolenaar1e607892006-03-13 22:15:53 +00003393 u_freeentries(buf, uhp, uhpp);
3394}
3395
3396/*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003397 * Free an alternate branch and any following alternate branches.
Bram Moolenaar1e607892006-03-13 22:15:53 +00003398 */
3399 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003400u_freebranch(
3401 buf_T *buf,
3402 u_header_T *uhp,
Bram Moolenaare38eab22019-12-05 21:50:01 +01003403 u_header_T **uhpp) // if not NULL reset when freeing this header
Bram Moolenaar1e607892006-03-13 22:15:53 +00003404{
3405 u_header_T *tofree, *next;
3406
Bram Moolenaare38eab22019-12-05 21:50:01 +01003407 // If this is the top branch we may need to use u_freeheader() to update
3408 // all the pointers.
Bram Moolenaar07d06772007-11-10 21:51:15 +00003409 if (uhp == buf->b_u_oldhead)
3410 {
Bram Moolenaaraa887322013-11-07 03:04:11 +01003411 while (buf->b_u_oldhead != NULL)
3412 u_freeheader(buf, buf->b_u_oldhead, uhpp);
Bram Moolenaar07d06772007-11-10 21:51:15 +00003413 return;
3414 }
3415
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003416 if (uhp->uh_alt_prev.ptr != NULL)
3417 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +00003418
3419 next = uhp;
3420 while (next != NULL)
3421 {
3422 tofree = next;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003423 if (tofree->uh_alt_next.ptr != NULL)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003424 u_freebranch(buf, tofree->uh_alt_next.ptr, uhpp); // recursive
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003425 next = tofree->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00003426 u_freeentries(buf, tofree, uhpp);
3427 }
3428}
3429
3430/*
3431 * Free all the undo entries for one header and the header itself.
3432 * This means that "uhp" is invalid when returning.
3433 */
3434 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003435u_freeentries(
3436 buf_T *buf,
3437 u_header_T *uhp,
Bram Moolenaare38eab22019-12-05 21:50:01 +01003438 u_header_T **uhpp) // if not NULL reset when freeing this header
Bram Moolenaar1e607892006-03-13 22:15:53 +00003439{
3440 u_entry_T *uep, *nuep;
3441
Bram Moolenaare38eab22019-12-05 21:50:01 +01003442 // Check for pointers to the header that become invalid now.
Bram Moolenaar1e607892006-03-13 22:15:53 +00003443 if (buf->b_u_curhead == uhp)
3444 buf->b_u_curhead = NULL;
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003445 if (buf->b_u_newhead == uhp)
Bram Moolenaare38eab22019-12-05 21:50:01 +01003446 buf->b_u_newhead = NULL; // freeing the newest entry
Bram Moolenaar1e607892006-03-13 22:15:53 +00003447 if (uhpp != NULL && uhp == *uhpp)
3448 *uhpp = NULL;
3449
3450 for (uep = uhp->uh_entry; uep != NULL; uep = nuep)
3451 {
3452 nuep = uep->ue_next;
3453 u_freeentry(uep, uep->ue_size);
3454 }
3455
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003456#ifdef U_DEBUG
3457 uhp->uh_magic = 0;
3458#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003459 vim_free((char_u *)uhp);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00003460 --buf->b_u_numhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003461}
3462
3463/*
3464 * free entry 'uep' and 'n' lines in uep->ue_array[]
3465 */
3466 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003467u_freeentry(u_entry_T *uep, long n)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003468{
Bram Moolenaar8d343302005-07-12 22:46:17 +00003469 while (n > 0)
Bram Moolenaarccae4672019-01-04 15:09:57 +01003470 vim_free(uep->ue_array[--n].ul_line);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003471 vim_free((char_u *)uep->ue_array);
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003472#ifdef U_DEBUG
3473 uep->ue_magic = 0;
3474#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003475 vim_free((char_u *)uep);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003476}
3477
3478/*
3479 * invalidate the undo buffer; called when storage has already been released
3480 */
Christian Brabandt9071ed82024-02-15 20:17:37 +01003481 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003482u_clearall(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003483{
3484 buf->b_u_newhead = buf->b_u_oldhead = buf->b_u_curhead = NULL;
3485 buf->b_u_synced = TRUE;
3486 buf->b_u_numhead = 0;
Bram Moolenaarccae4672019-01-04 15:09:57 +01003487 buf->b_u_line_ptr.ul_line = NULL;
3488 buf->b_u_line_ptr.ul_len = 0;
John Marriottfd1a8382024-11-06 21:21:50 +01003489 buf->b_u_line_ptr.ul_textlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003490 buf->b_u_line_lnum = 0;
3491}
3492
3493/*
Christian Brabandt9071ed82024-02-15 20:17:37 +01003494 * Free all allocated memory blocks for the buffer 'buf'.
3495 */
3496 static void
3497u_blockfree(buf_T *buf)
3498{
3499 while (buf->b_u_oldhead != NULL)
3500 u_freeheader(buf, buf->b_u_oldhead, NULL);
3501 vim_free(buf->b_u_line_ptr.ul_line);
3502}
3503
3504/*
3505 * Free all allocated memory blocks for the buffer 'buf'.
3506 * and invalidate the undo buffer
3507 */
3508 void
3509u_clearallandblockfree(buf_T *buf)
3510{
3511 u_blockfree(buf);
3512 u_clearall(buf);
3513}
3514
3515
3516
3517/*
Bram Moolenaarccae4672019-01-04 15:09:57 +01003518 * Save the line "lnum" for the "U" command.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003519 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02003520 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003521u_saveline(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003522{
Bram Moolenaare38eab22019-12-05 21:50:01 +01003523 if (lnum == curbuf->b_u_line_lnum) // line is already saved
Bram Moolenaar071d4272004-06-13 20:20:40 +00003524 return;
Bram Moolenaare38eab22019-12-05 21:50:01 +01003525 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) // should never happen
Bram Moolenaar071d4272004-06-13 20:20:40 +00003526 return;
3527 u_clearline();
3528 curbuf->b_u_line_lnum = lnum;
3529 if (curwin->w_cursor.lnum == lnum)
3530 curbuf->b_u_line_colnr = curwin->w_cursor.col;
3531 else
3532 curbuf->b_u_line_colnr = 0;
Bram Moolenaarccae4672019-01-04 15:09:57 +01003533 if (u_save_line(&curbuf->b_u_line_ptr, lnum) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003534 do_outofmem_msg((long_u)0);
3535}
3536
3537/*
3538 * clear the line saved for the "U" command
3539 * (this is used externally for crossing a line while in insert mode)
3540 */
3541 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003542u_clearline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003543{
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00003544 if (curbuf->b_u_line_ptr.ul_line == NULL)
3545 return;
3546
3547 VIM_CLEAR(curbuf->b_u_line_ptr.ul_line);
3548 curbuf->b_u_line_ptr.ul_len = 0;
John Marriottfd1a8382024-11-06 21:21:50 +01003549 curbuf->b_u_line_ptr.ul_textlen = 0;
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00003550 curbuf->b_u_line_lnum = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003551}
3552
3553/*
3554 * Implementation of the "U" command.
3555 * Differentiation from vi: "U" can be undone with the next "U".
3556 * We also allow the cursor to be in another line.
Bram Moolenaard04b7502010-07-08 22:27:55 +02003557 * Careful: may trigger autocommands that reload the buffer.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003558 */
3559 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003560u_undoline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003561{
Bram Moolenaarccae4672019-01-04 15:09:57 +01003562 colnr_T t;
3563 undoline_T oldp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003564
3565 if (undo_off)
3566 return;
3567
Bram Moolenaarccae4672019-01-04 15:09:57 +01003568 if (curbuf->b_u_line_ptr.ul_line == NULL
Bram Moolenaare3300c82008-02-13 14:21:38 +00003569 || curbuf->b_u_line_lnum > curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003570 {
3571 beep_flush();
3572 return;
3573 }
Bram Moolenaare3300c82008-02-13 14:21:38 +00003574
Bram Moolenaarccae4672019-01-04 15:09:57 +01003575 // first save the line for the 'u' command
Bram Moolenaar071d4272004-06-13 20:20:40 +00003576 if (u_savecommon(curbuf->b_u_line_lnum - 1,
Bram Moolenaar59f931e2010-07-24 20:27:03 +02003577 curbuf->b_u_line_lnum + 1, (linenr_T)0, FALSE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003578 return;
Bram Moolenaarccae4672019-01-04 15:09:57 +01003579 if (u_save_line(&oldp, curbuf->b_u_line_lnum) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003580 {
3581 do_outofmem_msg((long_u)0);
3582 return;
3583 }
Bram Moolenaar02c037a2020-08-30 19:26:45 +02003584 ml_replace_len(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr.ul_line,
3585 curbuf->b_u_line_ptr.ul_len, TRUE, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003586 changed_bytes(curbuf->b_u_line_lnum, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003587 curbuf->b_u_line_ptr = oldp;
3588
3589 t = curbuf->b_u_line_colnr;
3590 if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum)
3591 curbuf->b_u_line_colnr = curwin->w_cursor.col;
3592 curwin->w_cursor.col = t;
3593 curwin->w_cursor.lnum = curbuf->b_u_line_lnum;
Bram Moolenaare3300c82008-02-13 14:21:38 +00003594 check_cursor_col();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003595}
3596
3597/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003598 * Check if the 'modified' flag is set, or 'ff' has changed (only need to
3599 * check the first character, because it can only be "dos", "unix" or "mac").
3600 * "nofile" and "scratch" type buffers are considered to always be unchanged.
Bram Moolenaarf405c8f2017-12-09 19:51:49 +01003601 * Also considers a buffer changed when a terminal window contains a running
3602 * job.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003603 */
3604 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003605bufIsChanged(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003606{
Bram Moolenaareb44a682017-08-03 22:44:55 +02003607#ifdef FEAT_TERMINAL
Bram Moolenaar9e636b92022-05-29 22:37:05 +01003608 if (term_job_running_not_none(buf->b_term))
Bram Moolenaareb44a682017-08-03 22:44:55 +02003609 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003610#endif
Bram Moolenaarf405c8f2017-12-09 19:51:49 +01003611 return bufIsChangedNotTerm(buf);
3612}
3613
3614/*
Bram Moolenaara84a3dd2019-03-25 22:21:24 +01003615 * Return TRUE if any buffer has changes. Also buffers that are not written.
3616 */
3617 int
3618anyBufIsChanged(void)
3619{
3620 buf_T *buf;
3621
3622 FOR_ALL_BUFFERS(buf)
3623 if (bufIsChanged(buf))
3624 return TRUE;
Bram Moolenaard6c3f1f2019-03-26 00:31:21 +01003625 return FALSE;
Bram Moolenaara84a3dd2019-03-25 22:21:24 +01003626}
3627
3628/*
Bram Moolenaarf405c8f2017-12-09 19:51:49 +01003629 * Like bufIsChanged() but ignoring a terminal window.
3630 */
3631 int
3632bufIsChangedNotTerm(buf_T *buf)
3633{
Bram Moolenaar4551c0a2018-06-20 22:38:21 +02003634 // In a "prompt" buffer we do respect 'modified', so that we can control
3635 // closing the window by setting or resetting that option.
3636 return (!bt_dontwrite(buf) || bt_prompt(buf))
Bram Moolenaareb44a682017-08-03 22:44:55 +02003637 && (buf->b_changed || file_ff_differs(buf, TRUE));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003638}
3639
3640 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003641curbufIsChanged(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003642{
Bram Moolenaareb44a682017-08-03 22:44:55 +02003643 return bufIsChanged(curbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003644}
Bram Moolenaara800b422010-06-27 01:15:55 +02003645
3646#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003647
Bram Moolenaara800b422010-06-27 01:15:55 +02003648/*
3649 * For undotree(): Append the list of undo blocks at "first_uhp" to "list".
3650 * Recursive.
3651 */
Bram Moolenaar840d16f2019-09-10 21:27:18 +02003652 static void
Devin J. Pohly5fee1112023-04-23 20:26:59 -05003653u_eval_tree(buf_T *buf, u_header_T *first_uhp, list_T *list)
Bram Moolenaara800b422010-06-27 01:15:55 +02003654{
3655 u_header_T *uhp = first_uhp;
3656 dict_T *dict;
3657
3658 while (uhp != NULL)
3659 {
3660 dict = dict_alloc();
3661 if (dict == NULL)
3662 return;
Bram Moolenaare0be1672018-07-08 16:50:37 +02003663 dict_add_number(dict, "seq", uhp->uh_seq);
3664 dict_add_number(dict, "time", (long)uhp->uh_time);
Devin J. Pohly5fee1112023-04-23 20:26:59 -05003665 if (uhp == buf->b_u_newhead)
Bram Moolenaare0be1672018-07-08 16:50:37 +02003666 dict_add_number(dict, "newhead", 1);
Devin J. Pohly5fee1112023-04-23 20:26:59 -05003667 if (uhp == buf->b_u_curhead)
Bram Moolenaare0be1672018-07-08 16:50:37 +02003668 dict_add_number(dict, "curhead", 1);
Bram Moolenaara800b422010-06-27 01:15:55 +02003669 if (uhp->uh_save_nr > 0)
Bram Moolenaare0be1672018-07-08 16:50:37 +02003670 dict_add_number(dict, "save", uhp->uh_save_nr);
Bram Moolenaara800b422010-06-27 01:15:55 +02003671
3672 if (uhp->uh_alt_next.ptr != NULL)
3673 {
3674 list_T *alt_list = list_alloc();
3675
3676 if (alt_list != NULL)
3677 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003678 // Recursive call to add alternate undo tree.
Devin J. Pohly5fee1112023-04-23 20:26:59 -05003679 u_eval_tree(buf, uhp->uh_alt_next.ptr, alt_list);
Bram Moolenaara800b422010-06-27 01:15:55 +02003680 dict_add_list(dict, "alt", alt_list);
3681 }
3682 }
3683
3684 list_append_dict(list, dict);
3685 uhp = uhp->uh_prev.ptr;
3686 }
3687}
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003688
3689/*
3690 * "undofile(name)" function
3691 */
3692 void
Dominique Pellé0268ff32024-07-28 21:12:20 +02003693f_undofile(typval_T *argvars, typval_T *rettv)
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003694{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02003695 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
3696 return;
3697
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003698 rettv->v_type = VAR_STRING;
3699#ifdef FEAT_PERSISTENT_UNDO
3700 {
3701 char_u *fname = tv_get_string(&argvars[0]);
3702
3703 if (*fname == NUL)
3704 {
Bram Moolenaare38eab22019-12-05 21:50:01 +01003705 // If there is no file name there will be no undo file.
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003706 rettv->vval.v_string = NULL;
3707 }
3708 else
3709 {
3710 char_u *ffname = FullName_save(fname, TRUE);
3711
3712 if (ffname != NULL)
3713 rettv->vval.v_string = u_get_undo_file_name(ffname, FALSE);
3714 vim_free(ffname);
3715 }
3716 }
3717#else
3718 rettv->vval.v_string = NULL;
3719#endif
3720}
Christian Brabandt8a4c8122021-07-25 14:36:05 +02003721#ifdef FEAT_PERSISTENT_UNDO
3722/*
3723 * Reset undofile option and delete the undofile
3724 */
3725 void
3726u_undofile_reset_and_delete(buf_T *buf)
3727{
3728 char_u *file_name;
3729
3730 if (!buf->b_p_udf)
3731 return;
3732
3733 file_name = u_get_undo_file_name(buf->b_ffname, TRUE);
3734 if (file_name != NULL)
3735 {
3736 mch_remove(file_name);
3737 vim_free(file_name);
3738 }
3739
Bram Moolenaar31e5c602022-04-15 13:53:33 +01003740 set_option_value_give_err((char_u *)"undofile", 0L, NULL, OPT_LOCAL);
Christian Brabandt8a4c8122021-07-25 14:36:05 +02003741}
3742 #endif
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003743
3744/*
Devin J. Pohly5fee1112023-04-23 20:26:59 -05003745 * "undotree(expr)" function
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003746 */
3747 void
Dominique Pellé0268ff32024-07-28 21:12:20 +02003748f_undotree(typval_T *argvars, typval_T *rettv)
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003749{
Devin J. Pohly5fee1112023-04-23 20:26:59 -05003750 if (in_vim9script() && check_for_opt_buffer_arg(argvars, 0) == FAIL)
3751 return;
3752
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00003753 if (rettv_dict_alloc(rettv) == FAIL)
3754 return;
3755
zeertzjqab9f2ec2023-08-20 18:35:10 +02003756 typval_T *tv = &argvars[0];
3757 buf_T *buf = tv->v_type == VAR_UNKNOWN ? curbuf : get_buf_arg(tv);
3758 if (buf == NULL)
3759 return;
3760
3761 dict_T *dict = rettv->vval.v_dict;
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00003762
Devin J. Pohly5fee1112023-04-23 20:26:59 -05003763 dict_add_number(dict, "synced", (long)buf->b_u_synced);
3764 dict_add_number(dict, "seq_last", buf->b_u_seq_last);
3765 dict_add_number(dict, "save_last", buf->b_u_save_nr_last);
3766 dict_add_number(dict, "seq_cur", buf->b_u_seq_cur);
3767 dict_add_number(dict, "time_cur", (long)buf->b_u_time_cur);
3768 dict_add_number(dict, "save_cur", buf->b_u_save_nr_cur);
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00003769
zeertzjqab9f2ec2023-08-20 18:35:10 +02003770 list_T *list = list_alloc();
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00003771 if (list != NULL)
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003772 {
Devin J. Pohly5fee1112023-04-23 20:26:59 -05003773 u_eval_tree(buf, buf->b_u_oldhead, list);
Yegappan Lakshmanan142ed772023-01-26 12:00:00 +00003774 dict_add_list(dict, "entries", list);
Bram Moolenaar08c308a2019-09-04 17:48:15 +02003775 }
3776}
3777
Bram Moolenaara800b422010-06-27 01:15:55 +02003778#endif