blob: db6ea29ab781990a53878107bfa73f33d3dec663 [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001/* vi:set ts=8 sts=4 sw=4:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * undo.c: multi level undo facility
12 *
13 * The saved lines are stored in a list of lists (one for each buffer):
14 *
15 * b_u_oldhead------------------------------------------------+
16 * |
17 * V
18 * +--------------+ +--------------+ +--------------+
19 * b_u_newhead--->| u_header | | u_header | | u_header |
20 * | uh_next------>| uh_next------>| uh_next---->NULL
21 * NULL<--------uh_prev |<---------uh_prev |<---------uh_prev |
22 * | uh_entry | | uh_entry | | uh_entry |
23 * +--------|-----+ +--------|-----+ +--------|-----+
24 * | | |
25 * V V V
26 * +--------------+ +--------------+ +--------------+
27 * | u_entry | | u_entry | | u_entry |
28 * | ue_next | | ue_next | | ue_next |
29 * +--------|-----+ +--------|-----+ +--------|-----+
30 * | | |
31 * V V V
32 * +--------------+ NULL NULL
33 * | u_entry |
34 * | ue_next |
35 * +--------|-----+
36 * |
37 * V
38 * etc.
39 *
40 * Each u_entry list contains the information for one undo or redo.
41 * curbuf->b_u_curhead points to the header of the last undo (the next redo),
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +000042 * or is NULL if nothing has been undone (end of the branch).
Bram Moolenaar071d4272004-06-13 20:20:40 +000043 *
Bram Moolenaar1e607892006-03-13 22:15:53 +000044 * For keeping alternate undo/redo branches the uh_alt field is used. Thus at
45 * each point in the list a branch may appear for an alternate to redo. The
46 * uh_seq field is numbered sequentially to be able to find a newer or older
47 * branch.
48 *
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +000049 * +---------------+ +---------------+
50 * b_u_oldhead --->| u_header | | u_header |
51 * | uh_alt_next ---->| uh_alt_next ----> NULL
52 * NULL <----- uh_alt_prev |<------ uh_alt_prev |
53 * | uh_prev | | uh_prev |
54 * +-----|---------+ +-----|---------+
55 * | |
56 * V V
57 * +---------------+ +---------------+
58 * | u_header | | u_header |
59 * | uh_alt_next | | uh_alt_next |
60 * b_u_newhead --->| uh_alt_prev | | uh_alt_prev |
61 * | uh_prev | | uh_prev |
62 * +-----|---------+ +-----|---------+
63 * | |
64 * V V
65 * NULL +---------------+ +---------------+
66 * | u_header | | u_header |
67 * | uh_alt_next ---->| uh_alt_next |
68 * | uh_alt_prev |<------ uh_alt_prev |
69 * | uh_prev | | uh_prev |
70 * +-----|---------+ +-----|---------+
71 * | |
72 * etc. etc.
73 *
74 *
Bram Moolenaarf05e3b02010-05-29 15:40:47 +020075 * All data is allocated and will all be freed when the buffer is unloaded.
Bram Moolenaar071d4272004-06-13 20:20:40 +000076 */
77
Bram Moolenaarfecb6602007-10-01 20:54:15 +000078/* Uncomment the next line for including the u_check() function. This warns
79 * for errors in the debug information. */
80/* #define U_DEBUG 1 */
81#define UH_MAGIC 0x18dade /* value for uh_magic when in use */
82#define UE_MAGIC 0xabc123 /* value for ue_magic when in use */
83
Bram Moolenaar8f4ac012014-08-10 13:38:34 +020084/* Size of buffer used for encryption. */
85#define CRYPT_BUF_SIZE 8192
86
Bram Moolenaar071d4272004-06-13 20:20:40 +000087#include "vim.h"
88
Bram Moolenaar8f4ac012014-08-10 13:38:34 +020089/* Structure passed around between functions.
90 * Avoids passing cryptstate_T when encryption not available. */
91typedef struct {
92 buf_T *bi_buf;
93 FILE *bi_fp;
94#ifdef FEAT_CRYPT
95 cryptstate_T *bi_state;
96 char_u *bi_buffer; /* CRYPT_BUF_SIZE, NULL when not buffering */
97 size_t bi_used; /* bytes written to/read from bi_buffer */
98 size_t bi_avail; /* bytes available in bi_buffer */
99#endif
100} bufinfo_T;
101
102
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +0100103static long get_undolevel __ARGS((void));
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000104static void u_unch_branch __ARGS((u_header_T *uhp));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000105static u_entry_T *u_get_headentry __ARGS((void));
106static void u_getbot __ARGS((void));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000107static void u_doit __ARGS((int count));
Bram Moolenaarca003e12006-03-17 23:19:38 +0000108static void u_undoredo __ARGS((int undo));
Bram Moolenaardb552d602006-03-23 22:59:57 +0000109static void u_undo_end __ARGS((int did_undo, int absolute));
Bram Moolenaarefd2bf12006-03-16 21:41:35 +0000110static void u_add_time __ARGS((char_u *buf, size_t buflen, time_t tt));
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000111static void u_freeheader __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp));
Bram Moolenaar1e607892006-03-13 22:15:53 +0000112static void u_freebranch __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp));
113static void u_freeentries __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000114static void u_freeentry __ARGS((u_entry_T *, long));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200115#ifdef FEAT_PERSISTENT_UNDO
Bram Moolenaarf506c5b2010-06-22 06:28:58 +0200116static void corruption_error __ARGS((char *mesg, char_u *file_name));
Bram Moolenaar6a18eb62010-05-26 21:21:00 +0200117static void u_free_uhp __ARGS((u_header_T *uhp));
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200118static int undo_write __ARGS((bufinfo_T *bi, char_u *ptr, size_t len));
119static int undo_flush __ARGS((bufinfo_T *bi));
120static int fwrite_crypt __ARGS((bufinfo_T *bi, char_u *ptr, size_t len));
121static int undo_write_bytes __ARGS((bufinfo_T *bi, long_u nr, int len));
122static void put_header_ptr __ARGS((bufinfo_T *bi, u_header_T *uhp));
123static int undo_read_4c __ARGS((bufinfo_T *bi));
124static int undo_read_2c __ARGS((bufinfo_T *bi));
125static int undo_read_byte __ARGS((bufinfo_T *bi));
126static time_t undo_read_time __ARGS((bufinfo_T *bi));
127static int undo_read __ARGS((bufinfo_T *bi, char_u *buffer, size_t size));
128static char_u *read_string_decrypt __ARGS((bufinfo_T *bi, int len));
129static int serialize_header __ARGS((bufinfo_T *bi, char_u *hash));
130static int serialize_uhp __ARGS((bufinfo_T *bi, u_header_T *uhp));
131static u_header_T *unserialize_uhp __ARGS((bufinfo_T *bi, char_u *file_name));
132static int serialize_uep __ARGS((bufinfo_T *bi, u_entry_T *uep));
133static u_entry_T *unserialize_uep __ARGS((bufinfo_T *bi, int *error, char_u *file_name));
134static void serialize_pos __ARGS((bufinfo_T *bi, pos_T pos));
135static void unserialize_pos __ARGS((bufinfo_T *bi, pos_T *pos));
136static void serialize_visualinfo __ARGS((bufinfo_T *bi, visualinfo_T *info));
137static void unserialize_visualinfo __ARGS((bufinfo_T *bi, visualinfo_T *info));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200138#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000139
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200140#define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000141static char_u *u_save_line __ARGS((linenr_T));
142
Bram Moolenaar730cde92010-06-27 05:18:54 +0200143/* used in undo_end() to report number of added and deleted lines */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000144static long u_newcount, u_oldcount;
145
146/*
147 * When 'u' flag included in 'cpoptions', we behave like vi. Need to remember
148 * the action that "u" should do.
149 */
150static int undo_undoes = FALSE;
151
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200152static int lastmark = 0;
153
Bram Moolenaar9db58062010-05-29 20:33:07 +0200154#if defined(U_DEBUG) || defined(PROTO)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000155/*
156 * Check the undo structures for being valid. Print a warning when something
157 * looks wrong.
158 */
159static int seen_b_u_curhead;
160static int seen_b_u_newhead;
161static int header_count;
162
163 static void
164u_check_tree(u_header_T *uhp,
165 u_header_T *exp_uh_next,
166 u_header_T *exp_uh_alt_prev)
167{
168 u_entry_T *uep;
169
170 if (uhp == NULL)
171 return;
172 ++header_count;
173 if (uhp == curbuf->b_u_curhead && ++seen_b_u_curhead > 1)
174 {
175 EMSG("b_u_curhead found twice (looping?)");
176 return;
177 }
178 if (uhp == curbuf->b_u_newhead && ++seen_b_u_newhead > 1)
179 {
180 EMSG("b_u_newhead found twice (looping?)");
181 return;
182 }
183
184 if (uhp->uh_magic != UH_MAGIC)
185 EMSG("uh_magic wrong (may be using freed memory)");
186 else
187 {
188 /* Check pointers back are correct. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200189 if (uhp->uh_next.ptr != exp_uh_next)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000190 {
191 EMSG("uh_next wrong");
192 smsg((char_u *)"expected: 0x%x, actual: 0x%x",
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200193 exp_uh_next, uhp->uh_next.ptr);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000194 }
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200195 if (uhp->uh_alt_prev.ptr != exp_uh_alt_prev)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000196 {
197 EMSG("uh_alt_prev wrong");
198 smsg((char_u *)"expected: 0x%x, actual: 0x%x",
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200199 exp_uh_alt_prev, uhp->uh_alt_prev.ptr);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000200 }
201
202 /* Check the undo tree at this header. */
203 for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
204 {
205 if (uep->ue_magic != UE_MAGIC)
206 {
207 EMSG("ue_magic wrong (may be using freed memory)");
208 break;
209 }
210 }
211
212 /* Check the next alt tree. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200213 u_check_tree(uhp->uh_alt_next.ptr, uhp->uh_next.ptr, uhp);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000214
215 /* Check the next header in this branch. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200216 u_check_tree(uhp->uh_prev.ptr, uhp, NULL);
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000217 }
218}
219
Bram Moolenaarb0b50882010-07-07 18:26:28 +0200220 static void
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000221u_check(int newhead_may_be_NULL)
222{
223 seen_b_u_newhead = 0;
224 seen_b_u_curhead = 0;
225 header_count = 0;
226
227 u_check_tree(curbuf->b_u_oldhead, NULL, NULL);
228
229 if (seen_b_u_newhead == 0 && curbuf->b_u_oldhead != NULL
230 && !(newhead_may_be_NULL && curbuf->b_u_newhead == NULL))
231 EMSGN("b_u_newhead invalid: 0x%x", curbuf->b_u_newhead);
232 if (curbuf->b_u_curhead != NULL && seen_b_u_curhead == 0)
233 EMSGN("b_u_curhead invalid: 0x%x", curbuf->b_u_curhead);
234 if (header_count != curbuf->b_u_numhead)
235 {
236 EMSG("b_u_numhead invalid");
237 smsg((char_u *)"expected: %ld, actual: %ld",
238 (long)header_count, (long)curbuf->b_u_numhead);
239 }
240}
241#endif
242
Bram Moolenaar071d4272004-06-13 20:20:40 +0000243/*
Bram Moolenaard857f0e2005-06-21 22:37:39 +0000244 * Save the current line for both the "u" and "U" command.
Bram Moolenaar687a29c2013-04-15 15:47:12 +0200245 * Careful: may trigger autocommands that reload the buffer.
Bram Moolenaard857f0e2005-06-21 22:37:39 +0000246 * Returns OK or FAIL.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000247 */
248 int
249u_save_cursor()
250{
251 return (u_save((linenr_T)(curwin->w_cursor.lnum - 1),
252 (linenr_T)(curwin->w_cursor.lnum + 1)));
253}
254
255/*
256 * Save the lines between "top" and "bot" for both the "u" and "U" command.
257 * "top" may be 0 and bot may be curbuf->b_ml.ml_line_count + 1.
Bram Moolenaard04b7502010-07-08 22:27:55 +0200258 * Careful: may trigger autocommands that reload the buffer.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000259 * Returns FAIL when lines could not be saved, OK otherwise.
260 */
261 int
262u_save(top, bot)
263 linenr_T top, bot;
264{
265 if (undo_off)
266 return OK;
267
Bram Moolenaar687a29c2013-04-15 15:47:12 +0200268 if (top > curbuf->b_ml.ml_line_count
269 || top >= bot
270 || bot > curbuf->b_ml.ml_line_count + 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000271 return FALSE; /* rely on caller to do error messages */
272
273 if (top + 2 == bot)
274 u_saveline((linenr_T)(top + 1));
275
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200276 return (u_savecommon(top, bot, (linenr_T)0, FALSE));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000277}
278
279/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200280 * Save the line "lnum" (used by ":s" and "~" command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000281 * The line is replaced, so the new bottom line is lnum + 1.
Bram Moolenaard04b7502010-07-08 22:27:55 +0200282 * Careful: may trigger autocommands that reload the buffer.
283 * Returns FAIL when lines could not be saved, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000284 */
285 int
286u_savesub(lnum)
287 linenr_T lnum;
288{
289 if (undo_off)
290 return OK;
291
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200292 return (u_savecommon(lnum - 1, lnum + 1, lnum + 1, FALSE));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000293}
294
295/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200296 * A new line is inserted before line "lnum" (used by :s command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000297 * The line is inserted, so the new bottom line is lnum + 1.
Bram Moolenaard04b7502010-07-08 22:27:55 +0200298 * Careful: may trigger autocommands that reload the buffer.
299 * Returns FAIL when lines could not be saved, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000300 */
301 int
302u_inssub(lnum)
303 linenr_T lnum;
304{
305 if (undo_off)
306 return OK;
307
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200308 return (u_savecommon(lnum - 1, lnum, lnum + 1, FALSE));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000309}
310
311/*
Bram Moolenaarfff2bee2010-05-15 13:56:02 +0200312 * Save the lines "lnum" - "lnum" + nlines (used by delete command).
Bram Moolenaar071d4272004-06-13 20:20:40 +0000313 * The lines are deleted, so the new bottom line is lnum, unless the buffer
314 * becomes empty.
Bram Moolenaard04b7502010-07-08 22:27:55 +0200315 * Careful: may trigger autocommands that reload the buffer.
316 * Returns FAIL when lines could not be saved, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000317 */
318 int
319u_savedel(lnum, nlines)
320 linenr_T lnum;
321 long nlines;
322{
323 if (undo_off)
324 return OK;
325
326 return (u_savecommon(lnum - 1, lnum + nlines,
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200327 nlines == curbuf->b_ml.ml_line_count ? 2 : lnum, FALSE));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000328}
329
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000330/*
331 * Return TRUE when undo is allowed. Otherwise give an error message and
332 * return FALSE.
333 */
Bram Moolenaarce6ef252006-07-12 19:49:41 +0000334 int
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000335undo_allowed()
336{
337 /* Don't allow changes when 'modifiable' is off. */
338 if (!curbuf->b_p_ma)
339 {
340 EMSG(_(e_modifiable));
341 return FALSE;
342 }
343
344#ifdef HAVE_SANDBOX
345 /* In the sandbox it's not allowed to change the text. */
346 if (sandbox != 0)
347 {
348 EMSG(_(e_sandbox));
349 return FALSE;
350 }
351#endif
352
353 /* Don't allow changes in the buffer while editing the cmdline. The
354 * caller of getcmdline() may get confused. */
Bram Moolenaarb71eaae2006-01-20 23:10:18 +0000355 if (textlock != 0)
Bram Moolenaar8ada17c2006-01-19 22:16:24 +0000356 {
357 EMSG(_(e_secure));
358 return FALSE;
359 }
360
361 return TRUE;
362}
363
Bram Moolenaarb0b50882010-07-07 18:26:28 +0200364/*
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +0100365 * Get the undolevle value for the current buffer.
366 */
367 static long
368get_undolevel()
369{
370 if (curbuf->b_p_ul == NO_LOCAL_UNDOLEVEL)
371 return p_ul;
372 return curbuf->b_p_ul;
373}
374
375/*
Bram Moolenaarb0b50882010-07-07 18:26:28 +0200376 * Common code for various ways to save text before a change.
Bram Moolenaard04b7502010-07-08 22:27:55 +0200377 * "top" is the line above the first changed line.
378 * "bot" is the line below the last changed line.
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200379 * "newbot" is the new bottom line. Use zero when not known.
380 * "reload" is TRUE when saving for a buffer reload.
Bram Moolenaard04b7502010-07-08 22:27:55 +0200381 * Careful: may trigger autocommands that reload the buffer.
382 * Returns FAIL when lines could not be saved, OK otherwise.
Bram Moolenaarb0b50882010-07-07 18:26:28 +0200383 */
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200384 int
385u_savecommon(top, bot, newbot, reload)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000386 linenr_T top, bot;
387 linenr_T newbot;
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200388 int reload;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000389{
Bram Moolenaar1e607892006-03-13 22:15:53 +0000390 linenr_T lnum;
391 long i;
392 u_header_T *uhp;
393 u_header_T *old_curhead;
394 u_entry_T *uep;
395 u_entry_T *prev_uep;
396 long size;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000397
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200398 if (!reload)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000399 {
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200400 /* When making changes is not allowed return FAIL. It's a crude way
401 * to make all change commands fail. */
402 if (!undo_allowed())
Bram Moolenaar009b2592004-10-24 19:18:58 +0000403 return FAIL;
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200404
405#ifdef FEAT_NETBEANS_INTG
406 /*
407 * Netbeans defines areas that cannot be modified. Bail out here when
408 * trying to change text in a guarded area.
409 */
410 if (netbeans_active())
Bram Moolenaar009b2592004-10-24 19:18:58 +0000411 {
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200412 if (netbeans_is_guarded(top, bot))
413 {
414 EMSG(_(e_guarded));
415 return FAIL;
416 }
417 if (curbuf->b_p_ro)
418 {
419 EMSG(_(e_nbreadonly));
420 return FAIL;
421 }
Bram Moolenaar009b2592004-10-24 19:18:58 +0000422 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000423#endif
424
425#ifdef FEAT_AUTOCMD
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200426 /*
427 * Saving text for undo means we are going to make a change. Give a
428 * warning for a read-only file before making the change, so that the
429 * FileChangedRO event can replace the buffer with a read-write version
430 * (e.g., obtained from a source control system).
431 */
432 change_warning(0);
433 if (bot > curbuf->b_ml.ml_line_count + 1)
434 {
435 /* This happens when the FileChangedRO autocommand changes the
436 * file in a way it becomes shorter. */
Bram Moolenaarb4d587c2014-01-23 18:12:49 +0100437 EMSG(_("E881: Line count changed unexpectedly"));
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200438 return FAIL;
439 }
440#endif
Bram Moolenaard04b7502010-07-08 22:27:55 +0200441 }
Bram Moolenaar59f931e2010-07-24 20:27:03 +0200442
443#ifdef U_DEBUG
444 u_check(FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000445#endif
446
447 size = bot - top - 1;
448
449 /*
Bram Moolenaarb0b50882010-07-07 18:26:28 +0200450 * If curbuf->b_u_synced == TRUE make a new header.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000451 */
452 if (curbuf->b_u_synced)
453 {
454#ifdef FEAT_JUMPLIST
455 /* Need to create new entry in b_changelist. */
456 curbuf->b_new_change = TRUE;
457#endif
458
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +0100459 if (get_undolevel() >= 0)
Bram Moolenaar1e607892006-03-13 22:15:53 +0000460 {
461 /*
462 * Make a new header entry. Do this first so that we don't mess
463 * up the undo info when out of memory.
464 */
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200465 uhp = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T));
Bram Moolenaar1e607892006-03-13 22:15:53 +0000466 if (uhp == NULL)
467 goto nomem;
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000468#ifdef U_DEBUG
469 uhp->uh_magic = UH_MAGIC;
470#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +0000471 }
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000472 else
473 uhp = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000474
Bram Moolenaar071d4272004-06-13 20:20:40 +0000475 /*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000476 * If we undid more than we redid, move the entry lists before and
477 * including curbuf->b_u_curhead to an alternate branch.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000478 */
Bram Moolenaar1e607892006-03-13 22:15:53 +0000479 old_curhead = curbuf->b_u_curhead;
480 if (old_curhead != NULL)
481 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200482 curbuf->b_u_newhead = old_curhead->uh_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000483 curbuf->b_u_curhead = NULL;
484 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000485
486 /*
487 * free headers to keep the size right
488 */
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +0100489 while (curbuf->b_u_numhead > get_undolevel()
490 && curbuf->b_u_oldhead != NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +0000491 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000492 u_header_T *uhfree = curbuf->b_u_oldhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000493
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000494 if (uhfree == old_curhead)
495 /* Can't reconnect the branch, delete all of it. */
496 u_freebranch(curbuf, uhfree, &old_curhead);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200497 else if (uhfree->uh_alt_next.ptr == NULL)
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000498 /* There is no branch, only free one header. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000499 u_freeheader(curbuf, uhfree, &old_curhead);
Bram Moolenaar1e607892006-03-13 22:15:53 +0000500 else
501 {
502 /* Free the oldest alternate branch as a whole. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200503 while (uhfree->uh_alt_next.ptr != NULL)
504 uhfree = uhfree->uh_alt_next.ptr;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000505 u_freebranch(curbuf, uhfree, &old_curhead);
Bram Moolenaar1e607892006-03-13 22:15:53 +0000506 }
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000507#ifdef U_DEBUG
508 u_check(TRUE);
509#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +0000510 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000511
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000512 if (uhp == NULL) /* no undo at all */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000513 {
Bram Moolenaar1e607892006-03-13 22:15:53 +0000514 if (old_curhead != NULL)
515 u_freebranch(curbuf, old_curhead, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000516 curbuf->b_u_synced = FALSE;
517 return OK;
518 }
519
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200520 uhp->uh_prev.ptr = NULL;
521 uhp->uh_next.ptr = curbuf->b_u_newhead;
522 uhp->uh_alt_next.ptr = old_curhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000523 if (old_curhead != NULL)
524 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200525 uhp->uh_alt_prev.ptr = old_curhead->uh_alt_prev.ptr;
526 if (uhp->uh_alt_prev.ptr != NULL)
527 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = uhp;
528 old_curhead->uh_alt_prev.ptr = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000529 if (curbuf->b_u_oldhead == old_curhead)
530 curbuf->b_u_oldhead = uhp;
531 }
Bram Moolenaar89ed3df2007-01-09 19:23:12 +0000532 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200533 uhp->uh_alt_prev.ptr = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000534 if (curbuf->b_u_newhead != NULL)
Bram Moolenaar83d09bb2010-06-01 19:58:08 +0200535 curbuf->b_u_newhead->uh_prev.ptr = uhp;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000536
Bram Moolenaarca003e12006-03-17 23:19:38 +0000537 uhp->uh_seq = ++curbuf->b_u_seq_last;
538 curbuf->b_u_seq_cur = uhp->uh_seq;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000539 uhp->uh_time = time(NULL);
Bram Moolenaara800b422010-06-27 01:15:55 +0200540 uhp->uh_save_nr = 0;
541 curbuf->b_u_time_cur = uhp->uh_time + 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000542
Bram Moolenaar1e607892006-03-13 22:15:53 +0000543 uhp->uh_walk = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000544 uhp->uh_entry = NULL;
545 uhp->uh_getbot_entry = NULL;
546 uhp->uh_cursor = curwin->w_cursor; /* save cursor pos. for undo */
547#ifdef FEAT_VIRTUALEDIT
548 if (virtual_active() && curwin->w_cursor.coladd > 0)
549 uhp->uh_cursor_vcol = getviscol();
550 else
551 uhp->uh_cursor_vcol = -1;
552#endif
553
554 /* save changed and buffer empty flag for undo */
555 uhp->uh_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
556 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
557
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000558 /* save named marks and Visual marks for undo */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000559 mch_memmove(uhp->uh_namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000560 uhp->uh_visual = curbuf->b_visual;
Bram Moolenaara23ccb82006-02-27 00:08:02 +0000561
Bram Moolenaar071d4272004-06-13 20:20:40 +0000562 curbuf->b_u_newhead = uhp;
563 if (curbuf->b_u_oldhead == NULL)
564 curbuf->b_u_oldhead = uhp;
565 ++curbuf->b_u_numhead;
566 }
567 else
568 {
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +0100569 if (get_undolevel() < 0) /* no undo at all */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000570 return OK;
571
572 /*
573 * When saving a single line, and it has been saved just before, it
574 * doesn't make sense saving it again. Saves a lot of memory when
575 * making lots of changes inside the same line.
576 * This is only possible if the previous change didn't increase or
577 * decrease the number of lines.
578 * Check the ten last changes. More doesn't make sense and takes too
579 * long.
580 */
581 if (size == 1)
582 {
583 uep = u_get_headentry();
584 prev_uep = NULL;
585 for (i = 0; i < 10; ++i)
586 {
587 if (uep == NULL)
588 break;
589
590 /* If lines have been inserted/deleted we give up.
591 * Also when the line was included in a multi-line save. */
592 if ((curbuf->b_u_newhead->uh_getbot_entry != uep
593 ? (uep->ue_top + uep->ue_size + 1
594 != (uep->ue_bot == 0
595 ? curbuf->b_ml.ml_line_count + 1
596 : uep->ue_bot))
597 : uep->ue_lcount != curbuf->b_ml.ml_line_count)
598 || (uep->ue_size > 1
599 && top >= uep->ue_top
600 && top + 2 <= uep->ue_top + uep->ue_size + 1))
601 break;
602
603 /* If it's the same line we can skip saving it again. */
604 if (uep->ue_size == 1 && uep->ue_top == top)
605 {
606 if (i > 0)
607 {
608 /* It's not the last entry: get ue_bot for the last
609 * entry now. Following deleted/inserted lines go to
610 * the re-used entry. */
611 u_getbot();
612 curbuf->b_u_synced = FALSE;
613
614 /* Move the found entry to become the last entry. The
615 * order of undo/redo doesn't matter for the entries
616 * we move it over, since they don't change the line
617 * count and don't include this line. It does matter
618 * for the found entry if the line count is changed by
619 * the executed command. */
620 prev_uep->ue_next = uep->ue_next;
621 uep->ue_next = curbuf->b_u_newhead->uh_entry;
622 curbuf->b_u_newhead->uh_entry = uep;
623 }
624
625 /* The executed command may change the line count. */
626 if (newbot != 0)
627 uep->ue_bot = newbot;
628 else if (bot > curbuf->b_ml.ml_line_count)
629 uep->ue_bot = 0;
630 else
631 {
632 uep->ue_lcount = curbuf->b_ml.ml_line_count;
633 curbuf->b_u_newhead->uh_getbot_entry = uep;
634 }
635 return OK;
636 }
637 prev_uep = uep;
638 uep = uep->ue_next;
639 }
640 }
641
642 /* find line number for ue_bot for previous u_save() */
643 u_getbot();
644 }
645
646#if !defined(UNIX) && !defined(DJGPP) && !defined(WIN32) && !defined(__EMX__)
647 /*
648 * With Amiga and MSDOS 16 bit we can't handle big undo's, because
649 * then u_alloc_line would have to allocate a block larger than 32K
650 */
651 if (size >= 8000)
652 goto nomem;
653#endif
654
655 /*
656 * add lines in front of entry list
657 */
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200658 uep = (u_entry_T *)U_ALLOC_LINE(sizeof(u_entry_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000659 if (uep == NULL)
660 goto nomem;
Bram Moolenaar7db5fc82010-05-24 11:59:29 +0200661 vim_memset(uep, 0, sizeof(u_entry_T));
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000662#ifdef U_DEBUG
663 uep->ue_magic = UE_MAGIC;
664#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000665
666 uep->ue_size = size;
667 uep->ue_top = top;
668 if (newbot != 0)
669 uep->ue_bot = newbot;
670 /*
671 * Use 0 for ue_bot if bot is below last line.
672 * Otherwise we have to compute ue_bot later.
673 */
674 else if (bot > curbuf->b_ml.ml_line_count)
675 uep->ue_bot = 0;
676 else
677 {
678 uep->ue_lcount = curbuf->b_ml.ml_line_count;
679 curbuf->b_u_newhead->uh_getbot_entry = uep;
680 }
681
Bram Moolenaar26a60b42005-02-22 08:49:11 +0000682 if (size > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000683 {
Bram Moolenaar26a60b42005-02-22 08:49:11 +0000684 if ((uep->ue_array = (char_u **)U_ALLOC_LINE(
Bram Moolenaarf05e3b02010-05-29 15:40:47 +0200685 sizeof(char_u *) * size)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000686 {
687 u_freeentry(uep, 0L);
688 goto nomem;
689 }
690 for (i = 0, lnum = top + 1; i < size; ++i)
691 {
Bram Moolenaarae5bce12005-08-15 21:41:48 +0000692 fast_breakcheck();
693 if (got_int)
694 {
695 u_freeentry(uep, i);
696 return FAIL;
697 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000698 if ((uep->ue_array[i] = u_save_line(lnum++)) == NULL)
699 {
700 u_freeentry(uep, i);
701 goto nomem;
702 }
703 }
704 }
Bram Moolenaarf461c8e2005-06-25 23:04:51 +0000705 else
706 uep->ue_array = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000707 uep->ue_next = curbuf->b_u_newhead->uh_entry;
708 curbuf->b_u_newhead->uh_entry = uep;
709 curbuf->b_u_synced = FALSE;
710 undo_undoes = FALSE;
711
Bram Moolenaarfecb6602007-10-01 20:54:15 +0000712#ifdef U_DEBUG
713 u_check(FALSE);
714#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000715 return OK;
716
717nomem:
718 msg_silent = 0; /* must display the prompt */
719 if (ask_yesno((char_u *)_("No undo possible; continue anyway"), TRUE)
720 == 'y')
721 {
722 undo_off = TRUE; /* will be reset when character typed */
723 return OK;
724 }
725 do_outofmem_msg((long_u)0);
726 return FAIL;
727}
728
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200729#if defined(FEAT_PERSISTENT_UNDO) || defined(PROTO)
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200730
Bram Moolenaar9db58062010-05-29 20:33:07 +0200731# define UF_START_MAGIC "Vim\237UnDo\345" /* magic at start of undofile */
732# define UF_START_MAGIC_LEN 9
733# define UF_HEADER_MAGIC 0x5fd0 /* magic at start of header */
734# define UF_HEADER_END_MAGIC 0xe7aa /* magic after last header */
735# define UF_ENTRY_MAGIC 0xf518 /* magic at start of entry */
736# define UF_ENTRY_END_MAGIC 0x3581 /* magic after last entry */
Bram Moolenaara800b422010-06-27 01:15:55 +0200737# define UF_VERSION 2 /* 2-byte undofile version number */
Bram Moolenaara800b422010-06-27 01:15:55 +0200738# define UF_VERSION_CRYPT 0x8002 /* idem, encrypted */
739
740/* extra fields for header */
741# define UF_LAST_SAVE_NR 1
742
743/* extra fields for uhp */
744# define UHP_SAVE_NR 1
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200745
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200746static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");
Bram Moolenaarcdf04202010-05-29 15:11:47 +0200747
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200748/*
749 * Compute the hash for the current buffer text into hash[UNDO_HASH_SIZE].
750 */
751 void
752u_compute_hash(hash)
753 char_u *hash;
754{
755 context_sha256_T ctx;
756 linenr_T lnum;
757 char_u *p;
758
759 sha256_start(&ctx);
Bram Moolenaarae7ba982011-12-08 15:14:09 +0100760 for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum)
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200761 {
762 p = ml_get(lnum);
Bram Moolenaar442b4222010-05-24 21:34:22 +0200763 sha256_update(&ctx, p, (UINT32_T)(STRLEN(p) + 1));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200764 }
765 sha256_finish(&ctx, hash);
766}
767
768/*
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200769 * Return an allocated string of the full path of the target undofile.
770 * When "reading" is TRUE find the file to read, go over all directories in
771 * 'undodir'.
772 * When "reading" is FALSE use the first name where the directory exists.
Bram Moolenaar9db58062010-05-29 20:33:07 +0200773 * Returns NULL when there is no place to write or no file to read.
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200774 */
Bram Moolenaara17d4c12010-05-30 18:30:36 +0200775 char_u *
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200776u_get_undo_file_name(buf_ffname, reading)
777 char_u *buf_ffname;
778 int reading;
779{
780 char_u *dirp;
781 char_u dir_name[IOSIZE + 1];
782 char_u *munged_name = NULL;
783 char_u *undo_file_name = NULL;
784 int dir_len;
785 char_u *p;
786 struct stat st;
787 char_u *ffname = buf_ffname;
788#ifdef HAVE_READLINK
789 char_u fname_buf[MAXPATHL];
790#endif
791
792 if (ffname == NULL)
793 return NULL;
794
795#ifdef HAVE_READLINK
796 /* Expand symlink in the file name, so that we put the undo file with the
797 * actual file instead of with the symlink. */
798 if (resolve_symlink(ffname, fname_buf) == OK)
799 ffname = fname_buf;
800#endif
801
802 /* Loop over 'undodir'. When reading find the first file that exists.
803 * When not reading use the first directory that exists or ".". */
804 dirp = p_udir;
805 while (*dirp != NUL)
806 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +0200807 dir_len = copy_option_part(&dirp, dir_name, IOSIZE, ",");
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200808 if (dir_len == 1 && dir_name[0] == '.')
809 {
810 /* Use same directory as the ffname,
811 * "dir/name" -> "dir/.name.un~" */
Bram Moolenaar442b4222010-05-24 21:34:22 +0200812 undo_file_name = vim_strnsave(ffname, (int)(STRLEN(ffname) + 5));
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200813 if (undo_file_name == NULL)
814 break;
815 p = gettail(undo_file_name);
Bram Moolenaar206f0112014-03-12 16:51:55 +0100816#ifdef VMS
817 /* VMS can not handle more than one dot in the filenames
818 * use "dir/name" -> "dir/_un_name" - add _un_
819 * at the beginning to keep the extension */
820 mch_memmove(p + 4, p, STRLEN(p) + 1);
821 mch_memmove(p, "_un_", 4);
822
823#else
824 /* Use same directory as the ffname,
825 * "dir/name" -> "dir/.name.un~" */
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200826 mch_memmove(p + 1, p, STRLEN(p) + 1);
827 *p = '.';
828 STRCAT(p, ".un~");
Bram Moolenaar206f0112014-03-12 16:51:55 +0100829#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +0200830 }
831 else
832 {
833 dir_name[dir_len] = NUL;
834 if (mch_isdir(dir_name))
835 {
836 if (munged_name == NULL)
837 {
838 munged_name = vim_strsave(ffname);
839 if (munged_name == NULL)
840 return NULL;
841 for (p = munged_name; *p != NUL; mb_ptr_adv(p))
842 if (vim_ispathsep(*p))
843 *p = '%';
844 }
845 undo_file_name = concat_fnames(dir_name, munged_name, TRUE);
846 }
847 }
848
849 /* When reading check if the file exists. */
850 if (undo_file_name != NULL && (!reading
851 || mch_stat((char *)undo_file_name, &st) >= 0))
852 break;
853 vim_free(undo_file_name);
854 undo_file_name = NULL;
855 }
856
857 vim_free(munged_name);
858 return undo_file_name;
859}
860
Bram Moolenaar9db58062010-05-29 20:33:07 +0200861 static void
Bram Moolenaarf506c5b2010-06-22 06:28:58 +0200862corruption_error(mesg, file_name)
863 char *mesg;
Bram Moolenaar9db58062010-05-29 20:33:07 +0200864 char_u *file_name;
865{
Bram Moolenaarf506c5b2010-06-22 06:28:58 +0200866 EMSG3(_("E825: Corrupted undo file (%s): %s"), mesg, file_name);
Bram Moolenaar9db58062010-05-29 20:33:07 +0200867}
868
869 static void
870u_free_uhp(uhp)
871 u_header_T *uhp;
872{
873 u_entry_T *nuep;
874 u_entry_T *uep;
875
876 uep = uhp->uh_entry;
877 while (uep != NULL)
878 {
879 nuep = uep->ue_next;
880 u_freeentry(uep, uep->ue_size);
881 uep = nuep;
882 }
883 vim_free(uhp);
884}
885
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200886/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200887 * Write a sequence of bytes to the undo file.
888 * Buffers and encrypts as needed.
889 * Returns OK or FAIL.
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200890 */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200891 static int
892undo_write(bi, ptr, len)
893 bufinfo_T *bi;
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200894 char_u *ptr;
895 size_t len;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200896{
897#ifdef FEAT_CRYPT
898 if (bi->bi_buffer != NULL)
899 {
900 size_t len_todo = len;
901 char_u *p = ptr;
902
903 while (bi->bi_used + len_todo >= CRYPT_BUF_SIZE)
904 {
905 size_t n = CRYPT_BUF_SIZE - bi->bi_used;
906
907 mch_memmove(bi->bi_buffer + bi->bi_used, p, n);
908 len_todo -= n;
909 p += n;
910 bi->bi_used = CRYPT_BUF_SIZE;
911 if (undo_flush(bi) == FAIL)
912 return FAIL;
913 }
914 if (len_todo > 0)
915 {
916 mch_memmove(bi->bi_buffer + bi->bi_used, p, len_todo);
917 bi->bi_used += len_todo;
918 }
919 return OK;
920 }
921#endif
922 if (fwrite(ptr, len, (size_t)1, bi->bi_fp) != 1)
923 return FAIL;
924 return OK;
925}
926
927#ifdef FEAT_CRYPT
928 static int
929undo_flush(bi)
930 bufinfo_T *bi;
931{
Bram Moolenaar5a669b92014-08-12 20:14:33 +0200932 if (bi->bi_buffer != NULL && bi->bi_used > 0)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200933 {
934 crypt_encode_inplace(bi->bi_state, bi->bi_buffer, bi->bi_used);
935 if (fwrite(bi->bi_buffer, bi->bi_used, (size_t)1, bi->bi_fp) != 1)
936 return FAIL;
937 bi->bi_used = 0;
938 }
939 return OK;
940}
941#endif
942
943/*
944 * Write "ptr[len]" and crypt the bytes when needed.
945 * Returns OK or FAIL.
946 */
947 static int
948fwrite_crypt(bi, ptr, len)
949 bufinfo_T *bi;
950 char_u *ptr;
951 size_t len;
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200952{
953#ifdef FEAT_CRYPT
954 char_u *copy;
955 char_u small_buf[100];
956 size_t i;
957
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200958 if (bi->bi_state != NULL && bi->bi_buffer == NULL)
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200959 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200960 /* crypting every piece of text separately */
961 if (len < 100)
962 copy = small_buf; /* no malloc()/free() for short strings */
963 else
964 {
965 copy = lalloc(len, FALSE);
966 if (copy == NULL)
967 return 0;
968 }
969 crypt_encode(bi->bi_state, ptr, len, copy);
970 i = fwrite(copy, len, (size_t)1, bi->bi_fp);
971 if (copy != small_buf)
972 vim_free(copy);
973 return i == 1 ? OK : FAIL;
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200974 }
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200975#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200976 return undo_write(bi, ptr, len);
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200977}
978
979/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200980 * Write a number, MSB first, in "len" bytes.
981 * Must match with undo_read_?c() functions.
982 * Returns OK or FAIL.
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200983 */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200984 static int
985undo_write_bytes(bi, nr, len)
986 bufinfo_T *bi;
987 long_u nr;
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200988 int len;
989{
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200990 char_u buf[8];
991 int i;
992 int bufi = 0;
Bram Moolenaar191e0a22010-06-14 01:39:13 +0200993
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200994 for (i = len - 1; i >= 0; --i)
Bram Moolenaar9b8f0212014-08-13 22:05:53 +0200995 buf[bufi++] = (char_u)(nr >> (i * 8));
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200996 return undo_write(bi, buf, (size_t)len);
997}
998
999/*
1000 * Write the pointer to an undo header. Instead of writing the pointer itself
1001 * we use the sequence number of the header. This is converted back to
1002 * pointers when reading. */
1003 static void
1004put_header_ptr(bi, uhp)
1005 bufinfo_T *bi;
1006 u_header_T *uhp;
1007{
1008 undo_write_bytes(bi, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
Bram Moolenaar191e0a22010-06-14 01:39:13 +02001009}
1010
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001011 static int
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001012undo_read_4c(bi)
1013 bufinfo_T *bi;
1014{
1015#ifdef FEAT_CRYPT
1016 if (bi->bi_buffer != NULL)
1017 {
1018 char_u buf[4];
1019 int n;
1020
1021 undo_read(bi, buf, (size_t)4);
1022 n = (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3];
1023 return n;
1024 }
1025#endif
1026 return get4c(bi->bi_fp);
1027}
1028
1029 static int
1030undo_read_2c(bi)
1031 bufinfo_T *bi;
1032{
1033#ifdef FEAT_CRYPT
1034 if (bi->bi_buffer != NULL)
1035 {
1036 char_u buf[2];
1037 int n;
1038
1039 undo_read(bi, buf, (size_t)2);
1040 n = (buf[0] << 8) + buf[1];
1041 return n;
1042 }
1043#endif
1044 return get2c(bi->bi_fp);
1045}
1046
1047 static int
1048undo_read_byte(bi)
1049 bufinfo_T *bi;
1050{
1051#ifdef FEAT_CRYPT
1052 if (bi->bi_buffer != NULL)
1053 {
1054 char_u buf[1];
1055
1056 undo_read(bi, buf, (size_t)1);
1057 return buf[0];
1058 }
1059#endif
1060 return getc(bi->bi_fp);
1061}
1062
1063 static time_t
1064undo_read_time(bi)
1065 bufinfo_T *bi;
1066{
1067#ifdef FEAT_CRYPT
1068 if (bi->bi_buffer != NULL)
1069 {
1070 char_u buf[8];
1071 time_t n = 0;
1072 int i;
1073
1074 undo_read(bi, buf, (size_t)8);
1075 for (i = 0; i < 8; ++i)
1076 n = (n << 8) + buf[i];
1077 return n;
1078 }
1079#endif
1080 return get8ctime(bi->bi_fp);
1081}
1082
1083/*
1084 * Read "buffer[size]" from the undo file.
1085 * Return OK or FAIL.
1086 */
1087 static int
1088undo_read(bi, buffer, size)
1089 bufinfo_T *bi;
1090 char_u *buffer;
1091 size_t size;
1092{
1093#ifdef FEAT_CRYPT
1094 if (bi->bi_buffer != NULL)
1095 {
Bram Moolenaar9b8f0212014-08-13 22:05:53 +02001096 int size_todo = (int)size;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001097 char_u *p = buffer;
1098
1099 while (size_todo > 0)
1100 {
1101 size_t n;
1102
1103 if (bi->bi_used >= bi->bi_avail)
1104 {
1105 n = fread(bi->bi_buffer, 1, (size_t)CRYPT_BUF_SIZE, bi->bi_fp);
1106 if (n <= 0)
1107 {
1108 /* Error may be checked for only later. Fill with zeros,
1109 * so that the reader won't use garbage. */
1110 vim_memset(p, 0, size_todo);
1111 return FAIL;
1112 }
1113 bi->bi_avail = n;
1114 bi->bi_used = 0;
1115 crypt_decode_inplace(bi->bi_state, bi->bi_buffer, bi->bi_avail);
1116 }
1117 n = size_todo;
1118 if (n > bi->bi_avail - bi->bi_used)
1119 n = bi->bi_avail - bi->bi_used;
1120 mch_memmove(p, bi->bi_buffer + bi->bi_used, n);
1121 bi->bi_used += n;
Bram Moolenaar9b8f0212014-08-13 22:05:53 +02001122 size_todo -= (int)n;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001123 p += n;
1124 }
1125 return OK;
1126 }
1127#endif
1128 if (fread(buffer, (size_t)size, 1, bi->bi_fp) != 1)
1129 return FAIL;
1130 return OK;
1131}
1132
1133/*
1134 * Read a string of length "len" from "bi->bi_fd".
1135 * "len" can be zero to allocate an empty line.
1136 * Decrypt the bytes if needed.
1137 * Append a NUL.
1138 * Returns a pointer to allocated memory or NULL for failure.
1139 */
1140 static char_u *
1141read_string_decrypt(bi, len)
1142 bufinfo_T *bi;
1143 int len;
1144{
1145 char_u *ptr = alloc((unsigned)len + 1);
1146
1147 if (ptr != NULL)
1148 {
1149 if (len > 0 && undo_read(bi, ptr, len) == FAIL)
1150 {
1151 vim_free(ptr);
1152 return NULL;
1153 }
1154 ptr[len] = NUL;
1155#ifdef FEAT_CRYPT
1156 if (bi->bi_state != NULL && bi->bi_buffer == NULL)
1157 crypt_decode_inplace(bi->bi_state, ptr, len);
1158#endif
1159 }
1160 return ptr;
1161}
1162
1163/*
1164 * Writes the (not encrypted) header and initializes encryption if needed.
1165 */
1166 static int
1167serialize_header(bi, hash)
1168 bufinfo_T *bi;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001169 char_u *hash;
1170{
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001171 int len;
1172 buf_T *buf = bi->bi_buf;
1173 FILE *fp = bi->bi_fp;
1174 char_u time_buf[8];
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001175
1176 /* Start writing, first the magic marker and undo info version. */
1177 if (fwrite(UF_START_MAGIC, (size_t)UF_START_MAGIC_LEN, (size_t)1, fp) != 1)
1178 return FAIL;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001179
1180 /* If the buffer is encrypted then all text bytes following will be
1181 * encrypted. Numbers and other info is not crypted. */
1182#ifdef FEAT_CRYPT
Bram Moolenaarfa0ff9a2010-07-25 16:05:19 +02001183 if (*buf->b_p_key != NUL)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001184 {
1185 char_u *header;
1186 int header_len;
1187
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001188 undo_write_bytes(bi, (long_u)UF_VERSION_CRYPT, 2);
1189 bi->bi_state = crypt_create_for_writing(crypt_get_method_nr(buf),
1190 buf->b_p_key, &header, &header_len);
1191 if (bi->bi_state == NULL)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001192 return FAIL;
Bram Moolenaarbbd6afe2010-06-02 20:32:23 +02001193 len = (int)fwrite(header, (size_t)header_len, (size_t)1, fp);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001194 vim_free(header);
1195 if (len != 1)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001196 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001197 crypt_free_state(bi->bi_state);
1198 bi->bi_state = NULL;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001199 return FAIL;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001200 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001201
1202 if (crypt_whole_undofile(crypt_get_method_nr(buf)))
1203 {
1204 bi->bi_buffer = alloc(CRYPT_BUF_SIZE);
1205 if (bi->bi_buffer == NULL)
1206 {
1207 crypt_free_state(bi->bi_state);
1208 bi->bi_state = NULL;
1209 return FAIL;
1210 }
1211 bi->bi_used = 0;
1212 }
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001213 }
1214 else
1215#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001216 undo_write_bytes(bi, (long_u)UF_VERSION, 2);
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001217
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001218
1219 /* Write a hash of the buffer text, so that we can verify it is still the
1220 * same when reading the buffer text. */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001221 if (undo_write(bi, hash, (size_t)UNDO_HASH_SIZE) == FAIL)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001222 return FAIL;
1223
1224 /* buffer-specific data */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001225 undo_write_bytes(bi, (long_u)buf->b_ml.ml_line_count, 4);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001226 len = buf->b_u_line_ptr != NULL ? (int)STRLEN(buf->b_u_line_ptr) : 0;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001227 undo_write_bytes(bi, (long_u)len, 4);
1228 if (len > 0 && fwrite_crypt(bi, buf->b_u_line_ptr, (size_t)len) == FAIL)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001229 return FAIL;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001230 undo_write_bytes(bi, (long_u)buf->b_u_line_lnum, 4);
1231 undo_write_bytes(bi, (long_u)buf->b_u_line_colnr, 4);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001232
1233 /* Undo structures header data */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001234 put_header_ptr(bi, buf->b_u_oldhead);
1235 put_header_ptr(bi, buf->b_u_newhead);
1236 put_header_ptr(bi, buf->b_u_curhead);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001237
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001238 undo_write_bytes(bi, (long_u)buf->b_u_numhead, 4);
1239 undo_write_bytes(bi, (long_u)buf->b_u_seq_last, 4);
1240 undo_write_bytes(bi, (long_u)buf->b_u_seq_cur, 4);
1241 time_to_bytes(buf->b_u_time_cur, time_buf);
1242 undo_write(bi, time_buf, 8);
Bram Moolenaara800b422010-06-27 01:15:55 +02001243
1244 /* Optional fields. */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001245 undo_write_bytes(bi, 4, 1);
1246 undo_write_bytes(bi, UF_LAST_SAVE_NR, 1);
1247 undo_write_bytes(bi, (long_u)buf->b_u_save_nr_last, 4);
Bram Moolenaara800b422010-06-27 01:15:55 +02001248
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001249 undo_write_bytes(bi, 0, 1); /* end marker */
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001250
1251 return OK;
1252}
1253
1254 static int
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001255serialize_uhp(bi, uhp)
1256 bufinfo_T *bi;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001257 u_header_T *uhp;
1258{
1259 int i;
1260 u_entry_T *uep;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001261 char_u time_buf[8];
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001262
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001263 if (undo_write_bytes(bi, (long_u)UF_HEADER_MAGIC, 2) == FAIL)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001264 return FAIL;
1265
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001266 put_header_ptr(bi, uhp->uh_next.ptr);
1267 put_header_ptr(bi, uhp->uh_prev.ptr);
1268 put_header_ptr(bi, uhp->uh_alt_next.ptr);
1269 put_header_ptr(bi, uhp->uh_alt_prev.ptr);
1270 undo_write_bytes(bi, uhp->uh_seq, 4);
1271 serialize_pos(bi, uhp->uh_cursor);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001272#ifdef FEAT_VIRTUALEDIT
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001273 undo_write_bytes(bi, (long_u)uhp->uh_cursor_vcol, 4);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001274#else
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001275 undo_write_bytes(bi, (long_u)0, 4);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001276#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001277 undo_write_bytes(bi, (long_u)uhp->uh_flags, 2);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001278 /* Assume NMARKS will stay the same. */
1279 for (i = 0; i < NMARKS; ++i)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001280 serialize_pos(bi, uhp->uh_namedm[i]);
1281 serialize_visualinfo(bi, &uhp->uh_visual);
1282 time_to_bytes(uhp->uh_time, time_buf);
1283 undo_write(bi, time_buf, 8);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001284
Bram Moolenaara800b422010-06-27 01:15:55 +02001285 /* Optional fields. */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001286 undo_write_bytes(bi, 4, 1);
1287 undo_write_bytes(bi, UHP_SAVE_NR, 1);
1288 undo_write_bytes(bi, (long_u)uhp->uh_save_nr, 4);
Bram Moolenaara800b422010-06-27 01:15:55 +02001289
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001290 undo_write_bytes(bi, 0, 1); /* end marker */
Bram Moolenaara800b422010-06-27 01:15:55 +02001291
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001292 /* Write all the entries. */
1293 for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
1294 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001295 undo_write_bytes(bi, (long_u)UF_ENTRY_MAGIC, 2);
1296 if (serialize_uep(bi, uep) == FAIL)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001297 return FAIL;
1298 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001299 undo_write_bytes(bi, (long_u)UF_ENTRY_END_MAGIC, 2);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001300 return OK;
1301}
1302
1303 static u_header_T *
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001304unserialize_uhp(bi, file_name)
1305 bufinfo_T *bi;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001306 char_u *file_name;
1307{
1308 u_header_T *uhp;
1309 int i;
1310 u_entry_T *uep, *last_uep;
1311 int c;
1312 int error;
1313
1314 uhp = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T));
1315 if (uhp == NULL)
1316 return NULL;
1317 vim_memset(uhp, 0, sizeof(u_header_T));
1318#ifdef U_DEBUG
1319 uhp->uh_magic = UH_MAGIC;
1320#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001321 uhp->uh_next.seq = undo_read_4c(bi);
1322 uhp->uh_prev.seq = undo_read_4c(bi);
1323 uhp->uh_alt_next.seq = undo_read_4c(bi);
1324 uhp->uh_alt_prev.seq = undo_read_4c(bi);
1325 uhp->uh_seq = undo_read_4c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001326 if (uhp->uh_seq <= 0)
1327 {
1328 corruption_error("uh_seq", file_name);
1329 vim_free(uhp);
1330 return NULL;
1331 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001332 unserialize_pos(bi, &uhp->uh_cursor);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001333#ifdef FEAT_VIRTUALEDIT
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001334 uhp->uh_cursor_vcol = undo_read_4c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001335#else
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001336 (void)undo_read_4c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001337#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001338 uhp->uh_flags = undo_read_2c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001339 for (i = 0; i < NMARKS; ++i)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001340 unserialize_pos(bi, &uhp->uh_namedm[i]);
1341 unserialize_visualinfo(bi, &uhp->uh_visual);
1342 uhp->uh_time = undo_read_time(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001343
Bram Moolenaara800b422010-06-27 01:15:55 +02001344 /* Optional fields. */
Bram Moolenaar69154f22010-07-18 21:42:34 +02001345 for (;;)
1346 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001347 int len = undo_read_byte(bi);
Bram Moolenaar69154f22010-07-18 21:42:34 +02001348 int what;
Bram Moolenaara800b422010-06-27 01:15:55 +02001349
Bram Moolenaar69154f22010-07-18 21:42:34 +02001350 if (len == 0)
1351 break;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001352 what = undo_read_byte(bi);
Bram Moolenaar69154f22010-07-18 21:42:34 +02001353 switch (what)
1354 {
1355 case UHP_SAVE_NR:
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001356 uhp->uh_save_nr = undo_read_4c(bi);
Bram Moolenaara800b422010-06-27 01:15:55 +02001357 break;
Bram Moolenaar69154f22010-07-18 21:42:34 +02001358 default:
1359 /* field not supported, skip */
1360 while (--len >= 0)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001361 (void)undo_read_byte(bi);
Bram Moolenaara800b422010-06-27 01:15:55 +02001362 }
Bram Moolenaar69154f22010-07-18 21:42:34 +02001363 }
Bram Moolenaara800b422010-06-27 01:15:55 +02001364
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001365 /* Unserialize the uep list. */
1366 last_uep = NULL;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001367 while ((c = undo_read_2c(bi)) == UF_ENTRY_MAGIC)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001368 {
1369 error = FALSE;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001370 uep = unserialize_uep(bi, &error, file_name);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001371 if (last_uep == NULL)
1372 uhp->uh_entry = uep;
1373 else
1374 last_uep->ue_next = uep;
1375 last_uep = uep;
1376 if (uep == NULL || error)
1377 {
1378 u_free_uhp(uhp);
1379 return NULL;
1380 }
1381 }
1382 if (c != UF_ENTRY_END_MAGIC)
1383 {
1384 corruption_error("entry end", file_name);
1385 u_free_uhp(uhp);
1386 return NULL;
1387 }
1388
1389 return uhp;
1390}
1391
Bram Moolenaar9db58062010-05-29 20:33:07 +02001392/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001393 * Serialize "uep".
Bram Moolenaar9db58062010-05-29 20:33:07 +02001394 */
1395 static int
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001396serialize_uep(bi, uep)
1397 bufinfo_T *bi;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001398 u_entry_T *uep;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001399{
1400 int i;
1401 size_t len;
1402
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001403 undo_write_bytes(bi, (long_u)uep->ue_top, 4);
1404 undo_write_bytes(bi, (long_u)uep->ue_bot, 4);
1405 undo_write_bytes(bi, (long_u)uep->ue_lcount, 4);
1406 undo_write_bytes(bi, (long_u)uep->ue_size, 4);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001407 for (i = 0; i < uep->ue_size; ++i)
1408 {
1409 len = STRLEN(uep->ue_array[i]);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001410 if (undo_write_bytes(bi, (long_u)len, 4) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001411 return FAIL;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001412 if (len > 0 && fwrite_crypt(bi, uep->ue_array[i], len) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001413 return FAIL;
1414 }
1415 return OK;
1416}
1417
1418 static u_entry_T *
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001419unserialize_uep(bi, error, file_name)
1420 bufinfo_T *bi;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001421 int *error;
1422 char_u *file_name;
1423{
1424 int i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001425 u_entry_T *uep;
1426 char_u **array;
1427 char_u *line;
1428 int line_len;
1429
1430 uep = (u_entry_T *)U_ALLOC_LINE(sizeof(u_entry_T));
1431 if (uep == NULL)
1432 return NULL;
1433 vim_memset(uep, 0, sizeof(u_entry_T));
1434#ifdef U_DEBUG
1435 uep->ue_magic = UE_MAGIC;
1436#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001437 uep->ue_top = undo_read_4c(bi);
1438 uep->ue_bot = undo_read_4c(bi);
1439 uep->ue_lcount = undo_read_4c(bi);
1440 uep->ue_size = undo_read_4c(bi);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001441 if (uep->ue_size > 0)
1442 {
1443 array = (char_u **)U_ALLOC_LINE(sizeof(char_u *) * uep->ue_size);
1444 if (array == NULL)
1445 {
1446 *error = TRUE;
1447 return uep;
1448 }
1449 vim_memset(array, 0, sizeof(char_u *) * uep->ue_size);
1450 }
Bram Moolenaar644fdff2010-05-30 13:26:21 +02001451 else
1452 array = NULL;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001453 uep->ue_array = array;
1454
1455 for (i = 0; i < uep->ue_size; ++i)
1456 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001457 line_len = undo_read_4c(bi);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001458 if (line_len >= 0)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001459 line = read_string_decrypt(bi, line_len);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001460 else
1461 {
1462 line = NULL;
1463 corruption_error("line length", file_name);
1464 }
1465 if (line == NULL)
1466 {
1467 *error = TRUE;
1468 return uep;
1469 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001470 array[i] = line;
1471 }
1472 return uep;
1473}
1474
1475/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001476 * Serialize "pos".
Bram Moolenaar9db58062010-05-29 20:33:07 +02001477 */
1478 static void
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001479serialize_pos(bi, pos)
1480 bufinfo_T *bi;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001481 pos_T pos;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001482{
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001483 undo_write_bytes(bi, (long_u)pos.lnum, 4);
1484 undo_write_bytes(bi, (long_u)pos.col, 4);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001485#ifdef FEAT_VIRTUALEDIT
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001486 undo_write_bytes(bi, (long_u)pos.coladd, 4);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001487#else
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001488 undo_write_bytes(bi, (long_u)0, 4);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001489#endif
1490}
1491
1492/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001493 * Unserialize the pos_T at the current position.
Bram Moolenaar9db58062010-05-29 20:33:07 +02001494 */
1495 static void
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001496unserialize_pos(bi, pos)
1497 bufinfo_T *bi;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001498 pos_T *pos;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001499{
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001500 pos->lnum = undo_read_4c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001501 if (pos->lnum < 0)
1502 pos->lnum = 0;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001503 pos->col = undo_read_4c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001504 if (pos->col < 0)
1505 pos->col = 0;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001506#ifdef FEAT_VIRTUALEDIT
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001507 pos->coladd = undo_read_4c(bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001508 if (pos->coladd < 0)
1509 pos->coladd = 0;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001510#else
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001511 (void)undo_read_4c(bi);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001512#endif
1513}
1514
1515/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001516 * Serialize "info".
Bram Moolenaar9db58062010-05-29 20:33:07 +02001517 */
1518 static void
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001519serialize_visualinfo(bi, info)
1520 bufinfo_T *bi;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001521 visualinfo_T *info;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001522{
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001523 serialize_pos(bi, info->vi_start);
1524 serialize_pos(bi, info->vi_end);
1525 undo_write_bytes(bi, (long_u)info->vi_mode, 4);
1526 undo_write_bytes(bi, (long_u)info->vi_curswant, 4);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001527}
1528
1529/*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001530 * Unserialize the visualinfo_T at the current position.
Bram Moolenaar9db58062010-05-29 20:33:07 +02001531 */
1532 static void
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001533unserialize_visualinfo(bi, info)
1534 bufinfo_T *bi;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001535 visualinfo_T *info;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001536{
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001537 unserialize_pos(bi, &info->vi_start);
1538 unserialize_pos(bi, &info->vi_end);
1539 info->vi_mode = undo_read_4c(bi);
1540 info->vi_curswant = undo_read_4c(bi);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001541}
1542
1543/*
1544 * Write the undo tree in an undo file.
1545 * When "name" is not NULL, use it as the name of the undo file.
1546 * Otherwise use buf->b_ffname to generate the undo file name.
1547 * "buf" must never be null, buf->b_ffname is used to obtain the original file
1548 * permissions.
1549 * "forceit" is TRUE for ":wundo!", FALSE otherwise.
1550 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
1551 */
1552 void
1553u_write_undo(name, forceit, buf, hash)
1554 char_u *name;
1555 int forceit;
1556 buf_T *buf;
1557 char_u *hash;
1558{
1559 u_header_T *uhp;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001560 char_u *file_name;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001561 int mark;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001562#ifdef U_DEBUG
1563 int headers_written = 0;
1564#endif
1565 int fd;
1566 FILE *fp = NULL;
1567 int perm;
1568 int write_ok = FALSE;
1569#ifdef UNIX
1570 int st_old_valid = FALSE;
1571 struct stat st_old;
1572 struct stat st_new;
1573#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001574 bufinfo_T bi;
1575
Bram Moolenaar5a669b92014-08-12 20:14:33 +02001576 vim_memset(&bi, 0, sizeof(bi));
Bram Moolenaar9db58062010-05-29 20:33:07 +02001577
1578 if (name == NULL)
1579 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001580 file_name = u_get_undo_file_name(buf->b_ffname, FALSE);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001581 if (file_name == NULL)
1582 {
1583 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001584 {
1585 verbose_enter();
1586 smsg((char_u *)
1587 _("Cannot write undo file in any directory in 'undodir'"));
1588 verbose_leave();
1589 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001590 return;
1591 }
1592 }
1593 else
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001594 file_name = name;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001595
1596 /*
1597 * Decide about the permission to use for the undo file. If the buffer
1598 * has a name use the permission of the original file. Otherwise only
1599 * allow the user to access the undo file.
1600 */
1601 perm = 0600;
1602 if (buf->b_ffname != NULL)
1603 {
1604#ifdef UNIX
1605 if (mch_stat((char *)buf->b_ffname, &st_old) >= 0)
1606 {
1607 perm = st_old.st_mode;
1608 st_old_valid = TRUE;
1609 }
1610#else
1611 perm = mch_getperm(buf->b_ffname);
1612 if (perm < 0)
1613 perm = 0600;
1614#endif
1615 }
1616
1617 /* strip any s-bit */
1618 perm = perm & 0777;
1619
1620 /* If the undo file already exists, verify that it actually is an undo
1621 * file, and delete it. */
1622 if (mch_getperm(file_name) >= 0)
1623 {
1624 if (name == NULL || !forceit)
1625 {
1626 /* Check we can read it and it's an undo file. */
1627 fd = mch_open((char *)file_name, O_RDONLY|O_EXTRA, 0);
1628 if (fd < 0)
1629 {
1630 if (name != NULL || p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001631 {
1632 if (name == NULL)
1633 verbose_enter();
1634 smsg((char_u *)
1635 _("Will not overwrite with undo file, cannot read: %s"),
Bram Moolenaar9db58062010-05-29 20:33:07 +02001636 file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001637 if (name == NULL)
1638 verbose_leave();
1639 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001640 goto theend;
1641 }
1642 else
1643 {
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001644 char_u mbuf[UF_START_MAGIC_LEN];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001645 int len;
1646
Bram Moolenaar540fc6f2010-12-17 16:27:16 +01001647 len = read_eintr(fd, mbuf, UF_START_MAGIC_LEN);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001648 close(fd);
1649 if (len < UF_START_MAGIC_LEN
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001650 || memcmp(mbuf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001651 {
1652 if (name != NULL || p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001653 {
1654 if (name == NULL)
1655 verbose_enter();
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001656 smsg((char_u *)
1657 _("Will not overwrite, this is not an undo file: %s"),
Bram Moolenaar9db58062010-05-29 20:33:07 +02001658 file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001659 if (name == NULL)
1660 verbose_leave();
1661 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001662 goto theend;
1663 }
1664 }
1665 }
1666 mch_remove(file_name);
1667 }
1668
Bram Moolenaar504a8212010-05-30 17:17:42 +02001669 /* If there is no undo information at all, quit here after deleting any
1670 * existing undo file. */
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001671 if (buf->b_u_numhead == 0 && buf->b_u_line_ptr == NULL)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001672 {
1673 if (p_verbose > 0)
Bram Moolenaar97ea5112010-06-12 06:46:44 +02001674 verb_msg((char_u *)_("Skipping undo file write, nothing to undo"));
Bram Moolenaar504a8212010-05-30 17:17:42 +02001675 goto theend;
1676 }
1677
Bram Moolenaar9db58062010-05-29 20:33:07 +02001678 fd = mch_open((char *)file_name,
1679 O_CREAT|O_EXTRA|O_WRONLY|O_EXCL|O_NOFOLLOW, perm);
1680 if (fd < 0)
1681 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001682 EMSG2(_(e_not_open), file_name);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001683 goto theend;
1684 }
1685 (void)mch_setperm(file_name, perm);
1686 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001687 {
1688 verbose_enter();
Bram Moolenaar9db58062010-05-29 20:33:07 +02001689 smsg((char_u *)_("Writing undo file: %s"), file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001690 verbose_leave();
1691 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001692
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001693#ifdef U_DEBUG
Bram Moolenaar504a8212010-05-30 17:17:42 +02001694 /* Check there is no problem in undo info before writing. */
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001695 u_check(FALSE);
1696#endif
1697
Bram Moolenaar9db58062010-05-29 20:33:07 +02001698#ifdef UNIX
1699 /*
1700 * Try to set the group of the undo file same as the original file. If
1701 * this fails, set the protection bits for the group same as the
1702 * protection bits for others.
1703 */
Bram Moolenaarce69e822010-07-21 20:31:07 +02001704 if (st_old_valid
1705 && mch_stat((char *)file_name, &st_new) >= 0
1706 && st_new.st_gid != st_old.st_gid
Bram Moolenaar9db58062010-05-29 20:33:07 +02001707# ifdef HAVE_FCHOWN /* sequent-ptx lacks fchown() */
Bram Moolenaarce69e822010-07-21 20:31:07 +02001708 && fchown(fd, (uid_t)-1, st_old.st_gid) != 0
Bram Moolenaar9db58062010-05-29 20:33:07 +02001709# endif
1710 )
1711 mch_setperm(file_name, (perm & 0707) | ((perm & 07) << 3));
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02001712# if defined(HAVE_SELINUX) || defined(HAVE_SMACK)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001713 if (buf->b_ffname != NULL)
1714 mch_copy_sec(buf->b_ffname, file_name);
1715# endif
1716#endif
1717
1718 fp = fdopen(fd, "w");
1719 if (fp == NULL)
1720 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001721 EMSG2(_(e_not_open), file_name);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001722 close(fd);
1723 mch_remove(file_name);
1724 goto theend;
1725 }
1726
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001727 /* Undo must be synced. */
1728 u_sync(TRUE);
1729
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001730 /*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001731 * Write the header. Initializes encryption, if enabled.
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001732 */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001733 bi.bi_buf = buf;
1734 bi.bi_fp = fp;
1735 if (serialize_header(&bi, hash) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001736 goto write_error;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001737
1738 /*
1739 * Iteratively serialize UHPs and their UEPs from the top down.
1740 */
1741 mark = ++lastmark;
1742 uhp = buf->b_u_oldhead;
1743 while (uhp != NULL)
1744 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001745 /* Serialize current UHP if we haven't seen it */
1746 if (uhp->uh_walk != mark)
1747 {
1748 uhp->uh_walk = mark;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001749#ifdef U_DEBUG
1750 ++headers_written;
1751#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001752 if (serialize_uhp(&bi, uhp) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001753 goto write_error;
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001754 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001755
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001756 /* Now walk through the tree - algorithm from undo_time(). */
1757 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != mark)
1758 uhp = uhp->uh_prev.ptr;
1759 else if (uhp->uh_alt_next.ptr != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001760 && uhp->uh_alt_next.ptr->uh_walk != mark)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001761 uhp = uhp->uh_alt_next.ptr;
1762 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001763 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001764 uhp = uhp->uh_next.ptr;
1765 else if (uhp->uh_alt_prev.ptr != NULL)
1766 uhp = uhp->uh_alt_prev.ptr;
1767 else
1768 uhp = uhp->uh_next.ptr;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001769 }
1770
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001771 if (undo_write_bytes(&bi, (long_u)UF_HEADER_END_MAGIC, 2) == OK)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001772 write_ok = TRUE;
1773#ifdef U_DEBUG
1774 if (headers_written != buf->b_u_numhead)
Bram Moolenaar570064c2013-06-10 20:25:10 +02001775 {
1776 EMSGN("Written %ld headers, ...", headers_written);
1777 EMSGN("... but numhead is %ld", buf->b_u_numhead);
1778 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001779#endif
1780
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001781#ifdef FEAT_CRYPT
1782 if (bi.bi_state != NULL && undo_flush(&bi) == FAIL)
1783 write_ok = FALSE;
1784#endif
1785
Bram Moolenaar9db58062010-05-29 20:33:07 +02001786write_error:
1787 fclose(fp);
1788 if (!write_ok)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001789 EMSG2(_("E829: write error in undo file: %s"), file_name);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001790
1791#if defined(MACOS_CLASSIC) || defined(WIN3264)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001792 /* Copy file attributes; for systems where this can only be done after
1793 * closing the file. */
Bram Moolenaar9db58062010-05-29 20:33:07 +02001794 if (buf->b_ffname != NULL)
1795 (void)mch_copy_file_attribute(buf->b_ffname, file_name);
1796#endif
1797#ifdef HAVE_ACL
1798 if (buf->b_ffname != NULL)
1799 {
1800 vim_acl_T acl;
1801
1802 /* For systems that support ACL: get the ACL from the original file. */
1803 acl = mch_get_acl(buf->b_ffname);
1804 mch_set_acl(file_name, acl);
Bram Moolenaard2aed442012-06-01 13:46:12 +02001805 mch_free_acl(acl);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001806 }
1807#endif
1808
1809theend:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001810#ifdef FEAT_CRYPT
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001811 if (bi.bi_state != NULL)
1812 crypt_free_state(bi.bi_state);
1813 vim_free(bi.bi_buffer);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001814#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02001815 if (file_name != name)
1816 vim_free(file_name);
1817}
1818
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001819/*
1820 * Load the undo tree from an undo file.
1821 * If "name" is not NULL use it as the undo file name. This also means being
1822 * a bit more verbose.
1823 * Otherwise use curbuf->b_ffname to generate the undo file name.
1824 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
1825 */
1826 void
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001827u_read_undo(name, hash, orig_name)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001828 char_u *name;
1829 char_u *hash;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001830 char_u *orig_name;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001831{
1832 char_u *file_name;
1833 FILE *fp;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001834 long version, str_len;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001835 char_u *line_ptr = NULL;
1836 linenr_T line_lnum;
1837 colnr_T line_colnr;
1838 linenr_T line_count;
Bram Moolenaar442b4222010-05-24 21:34:22 +02001839 int num_head = 0;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001840 long old_header_seq, new_header_seq, cur_header_seq;
1841 long seq_last, seq_cur;
Bram Moolenaarb2c03502010-07-02 20:20:09 +02001842 long last_save_nr = 0;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001843 short old_idx = -1, new_idx = -1, cur_idx = -1;
1844 long num_read_uhps = 0;
1845 time_t seq_time;
1846 int i, j;
1847 int c;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001848 u_header_T *uhp;
1849 u_header_T **uhp_table = NULL;
1850 char_u read_hash[UNDO_HASH_SIZE];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001851 char_u magic_buf[UF_START_MAGIC_LEN];
1852#ifdef U_DEBUG
1853 int *uhp_table_used;
1854#endif
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001855#ifdef UNIX
1856 struct stat st_orig;
1857 struct stat st_undo;
1858#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001859 bufinfo_T bi;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001860
Bram Moolenaar5a669b92014-08-12 20:14:33 +02001861 vim_memset(&bi, 0, sizeof(bi));
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001862 if (name == NULL)
1863 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001864 file_name = u_get_undo_file_name(curbuf->b_ffname, TRUE);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001865 if (file_name == NULL)
1866 return;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001867
1868#ifdef UNIX
1869 /* For safety we only read an undo file if the owner is equal to the
Bram Moolenaar3b262392013-09-08 15:40:49 +02001870 * owner of the text file or equal to the current user. */
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001871 if (mch_stat((char *)orig_name, &st_orig) >= 0
1872 && mch_stat((char *)file_name, &st_undo) >= 0
Bram Moolenaar3b262392013-09-08 15:40:49 +02001873 && st_orig.st_uid != st_undo.st_uid
1874 && st_undo.st_uid != getuid())
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001875 {
1876 if (p_verbose > 0)
1877 {
1878 verbose_enter();
1879 smsg((char_u *)_("Not reading undo file, owner differs: %s"),
1880 file_name);
1881 verbose_leave();
1882 }
1883 return;
1884 }
1885#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001886 }
1887 else
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001888 file_name = name;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001889
1890 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001891 {
1892 verbose_enter();
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001893 smsg((char_u *)_("Reading undo file: %s"), file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001894 verbose_leave();
1895 }
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001896
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001897 fp = mch_fopen((char *)file_name, "r");
1898 if (fp == NULL)
1899 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001900 if (name != NULL || p_verbose > 0)
1901 EMSG2(_("E822: Cannot open undo file for reading: %s"), file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001902 goto error;
1903 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001904 bi.bi_buf = curbuf;
1905 bi.bi_fp = fp;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001906
Bram Moolenaar9db58062010-05-29 20:33:07 +02001907 /*
1908 * Read the undo file header.
1909 */
1910 if (fread(magic_buf, UF_START_MAGIC_LEN, 1, fp) != 1
1911 || memcmp(magic_buf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001912 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001913 EMSG2(_("E823: Not an undo file: %s"), file_name);
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001914 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001915 }
1916 version = get2c(fp);
Bram Moolenaar69154f22010-07-18 21:42:34 +02001917 if (version == UF_VERSION_CRYPT)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001918 {
1919#ifdef FEAT_CRYPT
Bram Moolenaar56be9502010-06-06 14:20:26 +02001920 if (*curbuf->b_p_key == NUL)
1921 {
1922 EMSG2(_("E832: Non-encrypted file has encrypted undo file: %s"),
1923 file_name);
1924 goto error;
1925 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001926 bi.bi_state = crypt_create_from_file(fp, curbuf->b_p_key);
1927 if (bi.bi_state == NULL)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001928 {
1929 EMSG2(_("E826: Undo file decryption failed: %s"), file_name);
1930 goto error;
1931 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001932 if (crypt_whole_undofile(bi.bi_state->method_nr))
1933 {
1934 bi.bi_buffer = alloc(CRYPT_BUF_SIZE);
1935 if (bi.bi_buffer == NULL)
1936 {
1937 crypt_free_state(bi.bi_state);
1938 bi.bi_state = NULL;
1939 goto error;
1940 }
1941 bi.bi_avail = 0;
1942 bi.bi_used = 0;
1943 }
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001944#else
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001945 EMSG2(_("E827: Undo file is encrypted: %s"), file_name);
1946 goto error;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001947#endif
1948 }
Bram Moolenaar69154f22010-07-18 21:42:34 +02001949 else if (version != UF_VERSION)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001950 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001951 EMSG2(_("E824: Incompatible undo file: %s"), file_name);
1952 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001953 }
1954
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001955 if (undo_read(&bi, read_hash, (size_t)UNDO_HASH_SIZE) == FAIL)
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001956 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001957 corruption_error("hash", file_name);
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001958 goto error;
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001959 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001960 line_count = (linenr_T)undo_read_4c(&bi);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001961 if (memcmp(hash, read_hash, UNDO_HASH_SIZE) != 0
1962 || line_count != curbuf->b_ml.ml_line_count)
1963 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001964 if (p_verbose > 0 || name != NULL)
1965 {
Bram Moolenaar504a8212010-05-30 17:17:42 +02001966 if (name == NULL)
1967 verbose_enter();
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001968 give_warning((char_u *)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001969 _("File contents changed, cannot use undo info"), TRUE);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001970 if (name == NULL)
1971 verbose_leave();
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001972 }
1973 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001974 }
1975
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001976 /* Read undo data for "U" command. */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001977 str_len = undo_read_4c(&bi);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001978 if (str_len < 0)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001979 goto error;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001980 if (str_len > 0)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001981 line_ptr = read_string_decrypt(&bi, str_len);
1982 line_lnum = (linenr_T)undo_read_4c(&bi);
1983 line_colnr = (colnr_T)undo_read_4c(&bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001984 if (line_lnum < 0 || line_colnr < 0)
1985 {
1986 corruption_error("line lnum/col", file_name);
1987 goto error;
1988 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001989
1990 /* Begin general undo data */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001991 old_header_seq = undo_read_4c(&bi);
1992 new_header_seq = undo_read_4c(&bi);
1993 cur_header_seq = undo_read_4c(&bi);
1994 num_head = undo_read_4c(&bi);
1995 seq_last = undo_read_4c(&bi);
1996 seq_cur = undo_read_4c(&bi);
1997 seq_time = undo_read_time(&bi);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001998
Bram Moolenaar69154f22010-07-18 21:42:34 +02001999 /* Optional header fields. */
2000 for (;;)
2001 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002002 int len = undo_read_byte(&bi);
Bram Moolenaar69154f22010-07-18 21:42:34 +02002003 int what;
Bram Moolenaara800b422010-06-27 01:15:55 +02002004
Bram Moolenaar69154f22010-07-18 21:42:34 +02002005 if (len == 0 || len == EOF)
2006 break;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002007 what = undo_read_byte(&bi);
Bram Moolenaar69154f22010-07-18 21:42:34 +02002008 switch (what)
2009 {
2010 case UF_LAST_SAVE_NR:
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002011 last_save_nr = undo_read_4c(&bi);
Bram Moolenaara800b422010-06-27 01:15:55 +02002012 break;
Bram Moolenaar69154f22010-07-18 21:42:34 +02002013 default:
2014 /* field not supported, skip */
2015 while (--len >= 0)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002016 (void)undo_read_byte(&bi);
Bram Moolenaara800b422010-06-27 01:15:55 +02002017 }
Bram Moolenaar69154f22010-07-18 21:42:34 +02002018 }
Bram Moolenaara800b422010-06-27 01:15:55 +02002019
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002020 /* uhp_table will store the freshly created undo headers we allocate
2021 * until we insert them into curbuf. The table remains sorted by the
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002022 * sequence numbers of the headers.
2023 * When there are no headers uhp_table is NULL. */
2024 if (num_head > 0)
2025 {
2026 uhp_table = (u_header_T **)U_ALLOC_LINE(
2027 num_head * sizeof(u_header_T *));
2028 if (uhp_table == NULL)
2029 goto error;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002030 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002031
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002032 while ((c = undo_read_2c(&bi)) == UF_HEADER_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002033 {
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02002034 if (num_read_uhps >= num_head)
2035 {
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02002036 corruption_error("num_head too small", file_name);
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02002037 goto error;
2038 }
2039
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002040 uhp = unserialize_uhp(&bi, file_name);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02002041 if (uhp == NULL)
2042 goto error;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02002043 uhp_table[num_read_uhps++] = uhp;
2044 }
2045
2046 if (num_read_uhps != num_head)
2047 {
2048 corruption_error("num_head", file_name);
2049 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002050 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02002051 if (c != UF_HEADER_END_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002052 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02002053 corruption_error("end marker", file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002054 goto error;
2055 }
2056
Bram Moolenaar9db58062010-05-29 20:33:07 +02002057#ifdef U_DEBUG
2058 uhp_table_used = (int *)alloc_clear(
2059 (unsigned)(sizeof(int) * num_head + 1));
2060# define SET_FLAG(j) ++uhp_table_used[j]
2061#else
2062# define SET_FLAG(j)
2063#endif
2064
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02002065 /* We have put all of the headers into a table. Now we iterate through the
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002066 * table and swizzle each sequence number we have stored in uh_*_seq into
2067 * a pointer corresponding to the header with that sequence number. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002068 for (i = 0; i < num_head; i++)
2069 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002070 uhp = uhp_table[i];
2071 if (uhp == NULL)
2072 continue;
2073 for (j = 0; j < num_head; j++)
2074 if (uhp_table[j] != NULL && i != j
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002075 && uhp_table[i]->uh_seq == uhp_table[j]->uh_seq)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002076 {
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02002077 corruption_error("duplicate uh_seq", file_name);
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002078 goto error;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02002079 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002080 for (j = 0; j < num_head; j++)
2081 if (uhp_table[j] != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002082 && uhp_table[j]->uh_seq == uhp->uh_next.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002083 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002084 uhp->uh_next.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02002085 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002086 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +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_prev.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002091 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002092 uhp->uh_prev.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_alt_next.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002099 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002100 uhp->uh_alt_next.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_prev.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002107 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002108 uhp->uh_alt_prev.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 if (old_header_seq > 0 && old_idx < 0 && uhp->uh_seq == old_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002113 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002114 old_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002115 SET_FLAG(i);
2116 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002117 if (new_header_seq > 0 && new_idx < 0 && uhp->uh_seq == new_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002118 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002119 new_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002120 SET_FLAG(i);
2121 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002122 if (cur_header_seq > 0 && cur_idx < 0 && uhp->uh_seq == cur_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002123 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002124 cur_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002125 SET_FLAG(i);
2126 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002127 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02002128
2129 /* Now that we have read the undo info successfully, free the current undo
2130 * info and use the info from the file. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002131 u_blockfree(curbuf);
Bram Moolenaar9db58062010-05-29 20:33:07 +02002132 curbuf->b_u_oldhead = old_idx < 0 ? NULL : uhp_table[old_idx];
2133 curbuf->b_u_newhead = new_idx < 0 ? NULL : uhp_table[new_idx];
2134 curbuf->b_u_curhead = cur_idx < 0 ? NULL : uhp_table[cur_idx];
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002135 curbuf->b_u_line_ptr = line_ptr;
2136 curbuf->b_u_line_lnum = line_lnum;
2137 curbuf->b_u_line_colnr = line_colnr;
2138 curbuf->b_u_numhead = num_head;
2139 curbuf->b_u_seq_last = seq_last;
2140 curbuf->b_u_seq_cur = seq_cur;
Bram Moolenaara800b422010-06-27 01:15:55 +02002141 curbuf->b_u_time_cur = seq_time;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002142 curbuf->b_u_save_nr_last = last_save_nr;
Bram Moolenaardba01a02010-11-03 19:32:42 +01002143 curbuf->b_u_save_nr_cur = last_save_nr;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02002144
2145 curbuf->b_u_synced = TRUE;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002146 vim_free(uhp_table);
Bram Moolenaar9db58062010-05-29 20:33:07 +02002147
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002148#ifdef U_DEBUG
Bram Moolenaar9db58062010-05-29 20:33:07 +02002149 for (i = 0; i < num_head; ++i)
2150 if (uhp_table_used[i] == 0)
2151 EMSGN("uhp_table entry %ld not used, leaking memory", i);
2152 vim_free(uhp_table_used);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002153 u_check(TRUE);
2154#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02002155
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002156 if (name != NULL)
2157 smsg((char_u *)_("Finished reading undo file %s"), file_name);
2158 goto theend;
2159
2160error:
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002161 vim_free(line_ptr);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002162 if (uhp_table != NULL)
2163 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002164 for (i = 0; i < num_read_uhps; i++)
2165 if (uhp_table[i] != NULL)
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02002166 u_free_uhp(uhp_table[i]);
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002167 vim_free(uhp_table);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002168 }
2169
2170theend:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002171#ifdef FEAT_CRYPT
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002172 if (bi.bi_state != NULL)
2173 crypt_free_state(bi.bi_state);
2174 vim_free(bi.bi_buffer);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002175#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002176 if (fp != NULL)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002177 fclose(fp);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002178 if (file_name != name)
2179 vim_free(file_name);
2180 return;
2181}
2182
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002183#endif /* FEAT_PERSISTENT_UNDO */
2184
2185
Bram Moolenaar071d4272004-06-13 20:20:40 +00002186/*
2187 * If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible).
2188 * If 'cpoptions' does not contain 'u': Always undo.
2189 */
2190 void
2191u_undo(count)
2192 int count;
2193{
2194 /*
2195 * If we get an undo command while executing a macro, we behave like the
2196 * original vi. If this happens twice in one macro the result will not
2197 * be compatible.
2198 */
2199 if (curbuf->b_u_synced == FALSE)
2200 {
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002201 u_sync(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002202 count = 1;
2203 }
2204
2205 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
2206 undo_undoes = TRUE;
2207 else
2208 undo_undoes = !undo_undoes;
2209 u_doit(count);
2210}
2211
2212/*
2213 * If 'cpoptions' contains 'u': Repeat the previous undo or redo.
2214 * If 'cpoptions' does not contain 'u': Always redo.
2215 */
2216 void
2217u_redo(count)
2218 int count;
2219{
2220 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
2221 undo_undoes = FALSE;
2222 u_doit(count);
2223}
2224
2225/*
2226 * Undo or redo, depending on 'undo_undoes', 'count' times.
2227 */
2228 static void
Bram Moolenaarca003e12006-03-17 23:19:38 +00002229u_doit(startcount)
2230 int startcount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002231{
Bram Moolenaarca003e12006-03-17 23:19:38 +00002232 int count = startcount;
2233
Bram Moolenaar8ada17c2006-01-19 22:16:24 +00002234 if (!undo_allowed())
Bram Moolenaar071d4272004-06-13 20:20:40 +00002235 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002236
2237 u_newcount = 0;
2238 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00002239 if (curbuf->b_ml.ml_flags & ML_EMPTY)
2240 u_oldcount = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002241 while (count--)
2242 {
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002243 /* Do the change warning now, so that it triggers FileChangedRO when
2244 * needed. This may cause the file to be reloaded, that must happen
2245 * before we do anything, because it may change curbuf->b_u_curhead
2246 * and more. */
2247 change_warning(0);
2248
Bram Moolenaar071d4272004-06-13 20:20:40 +00002249 if (undo_undoes)
2250 {
2251 if (curbuf->b_u_curhead == NULL) /* first undo */
2252 curbuf->b_u_curhead = curbuf->b_u_newhead;
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +01002253 else if (get_undolevel() > 0) /* multi level undo */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002254 /* get next undo */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002255 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002256 /* nothing to undo */
2257 if (curbuf->b_u_numhead == 0 || curbuf->b_u_curhead == NULL)
2258 {
2259 /* stick curbuf->b_u_curhead at end */
2260 curbuf->b_u_curhead = curbuf->b_u_oldhead;
2261 beep_flush();
Bram Moolenaarca003e12006-03-17 23:19:38 +00002262 if (count == startcount - 1)
2263 {
2264 MSG(_("Already at oldest change"));
2265 return;
2266 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002267 break;
2268 }
2269
Bram Moolenaarca003e12006-03-17 23:19:38 +00002270 u_undoredo(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002271 }
2272 else
2273 {
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +01002274 if (curbuf->b_u_curhead == NULL || get_undolevel() <= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002275 {
2276 beep_flush(); /* nothing to redo */
Bram Moolenaarca003e12006-03-17 23:19:38 +00002277 if (count == startcount - 1)
2278 {
2279 MSG(_("Already at newest change"));
2280 return;
2281 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002282 break;
2283 }
2284
Bram Moolenaarca003e12006-03-17 23:19:38 +00002285 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002286
2287 /* Advance for next redo. Set "newhead" when at the end of the
2288 * redoable changes. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002289 if (curbuf->b_u_curhead->uh_prev.ptr == NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002290 curbuf->b_u_newhead = curbuf->b_u_curhead;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002291 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002292 }
2293 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00002294 u_undo_end(undo_undoes, FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002295}
2296
Bram Moolenaar1e607892006-03-13 22:15:53 +00002297/*
2298 * Undo or redo over the timeline.
2299 * When "step" is negative go back in time, otherwise goes forward in time.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002300 * When "sec" is FALSE make "step" steps, when "sec" is TRUE use "step" as
2301 * seconds.
Bram Moolenaar730cde92010-06-27 05:18:54 +02002302 * When "file" is TRUE use "step" as a number of file writes.
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002303 * When "absolute" is TRUE use "step" as the sequence number to jump to.
2304 * "sec" must be FALSE then.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002305 */
2306 void
Bram Moolenaar730cde92010-06-27 05:18:54 +02002307undo_time(step, sec, file, absolute)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002308 long step;
2309 int sec;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002310 int file;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002311 int absolute;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002312{
2313 long target;
2314 long closest;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002315 long closest_start;
2316 long closest_seq = 0;
2317 long val;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002318 u_header_T *uhp;
2319 u_header_T *last;
2320 int mark;
2321 int nomark;
2322 int round;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002323 int dosec = sec;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002324 int dofile = file;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002325 int above = FALSE;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002326 int did_undo = TRUE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002327
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00002328 /* First make sure the current undoable change is synced. */
2329 if (curbuf->b_u_synced == FALSE)
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002330 u_sync(TRUE);
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00002331
Bram Moolenaar1e607892006-03-13 22:15:53 +00002332 u_newcount = 0;
2333 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00002334 if (curbuf->b_ml.ml_flags & ML_EMPTY)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002335 u_oldcount = -1;
2336
Bram Moolenaarca003e12006-03-17 23:19:38 +00002337 /* "target" is the node below which we want to be.
2338 * Init "closest" to a value we can't reach. */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002339 if (absolute)
2340 {
2341 target = step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002342 closest = -1;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002343 }
Bram Moolenaarca003e12006-03-17 23:19:38 +00002344 else
Bram Moolenaar1e607892006-03-13 22:15:53 +00002345 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002346 /* When doing computations with time_t subtract starttime, because
2347 * time_t converted to a long may result in a wrong number. */
Bram Moolenaar730cde92010-06-27 05:18:54 +02002348 if (dosec)
Bram Moolenaara800b422010-06-27 01:15:55 +02002349 target = (long)(curbuf->b_u_time_cur - starttime) + step;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002350 else if (dofile)
2351 {
2352 if (step < 0)
2353 {
2354 /* Going back to a previous write. If there were changes after
2355 * the last write, count that as moving one file-write, so
2356 * that ":earlier 1f" undoes all changes since the last save. */
2357 uhp = curbuf->b_u_curhead;
2358 if (uhp != NULL)
2359 uhp = uhp->uh_next.ptr;
2360 else
2361 uhp = curbuf->b_u_newhead;
2362 if (uhp != NULL && uhp->uh_save_nr != 0)
2363 /* "uh_save_nr" was set in the last block, that means
2364 * there were no changes since the last write */
2365 target = curbuf->b_u_save_nr_cur + step;
2366 else
2367 /* count the changes since the last write as one step */
2368 target = curbuf->b_u_save_nr_cur + step + 1;
2369 if (target <= 0)
2370 /* Go to before first write: before the oldest change. Use
2371 * the sequence number for that. */
2372 dofile = FALSE;
2373 }
2374 else
2375 {
2376 /* Moving forward to a newer write. */
2377 target = curbuf->b_u_save_nr_cur + step;
2378 if (target > curbuf->b_u_save_nr_last)
2379 {
2380 /* Go to after last write: after the latest change. Use
2381 * the sequence number for that. */
2382 target = curbuf->b_u_seq_last + 1;
2383 dofile = FALSE;
2384 }
2385 }
2386 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002387 else
2388 target = curbuf->b_u_seq_cur + step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002389 if (step < 0)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002390 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002391 if (target < 0)
2392 target = 0;
2393 closest = -1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002394 }
2395 else
2396 {
Bram Moolenaar730cde92010-06-27 05:18:54 +02002397 if (dosec)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002398 closest = (long)(time(NULL) - starttime + 1);
Bram Moolenaar730cde92010-06-27 05:18:54 +02002399 else if (dofile)
2400 closest = curbuf->b_u_save_nr_last + 2;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002401 else
2402 closest = curbuf->b_u_seq_last + 2;
2403 if (target >= closest)
2404 target = closest - 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002405 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002406 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002407 closest_start = closest;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002408 closest_seq = curbuf->b_u_seq_cur;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002409
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002410 /*
2411 * May do this twice:
Bram Moolenaar1e607892006-03-13 22:15:53 +00002412 * 1. Search for "target", update "closest" to the best match found.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002413 * 2. If "target" not found search for "closest".
2414 *
2415 * When using the closest time we use the sequence number in the second
2416 * round, because there may be several entries with the same time.
2417 */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002418 for (round = 1; round <= 2; ++round)
2419 {
2420 /* Find the path from the current state to where we want to go. The
2421 * desired state can be anywhere in the undo tree, need to go all over
2422 * it. We put "nomark" in uh_walk where we have been without success,
2423 * "mark" where it could possibly be. */
2424 mark = ++lastmark;
2425 nomark = ++lastmark;
2426
2427 if (curbuf->b_u_curhead == NULL) /* at leaf of the tree */
2428 uhp = curbuf->b_u_newhead;
2429 else
2430 uhp = curbuf->b_u_curhead;
2431
2432 while (uhp != NULL)
2433 {
2434 uhp->uh_walk = mark;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002435 if (dosec)
2436 val = (long)(uhp->uh_time - starttime);
2437 else if (dofile)
2438 val = uhp->uh_save_nr;
2439 else
2440 val = uhp->uh_seq;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002441
Bram Moolenaar730cde92010-06-27 05:18:54 +02002442 if (round == 1 && !(dofile && val == 0))
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002443 {
2444 /* Remember the header that is closest to the target.
2445 * It must be at least in the right direction (checked with
Bram Moolenaarca003e12006-03-17 23:19:38 +00002446 * "b_u_seq_cur"). When the timestamp is equal find the
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002447 * highest/lowest sequence number. */
Bram Moolenaarca003e12006-03-17 23:19:38 +00002448 if ((step < 0 ? uhp->uh_seq <= curbuf->b_u_seq_cur
2449 : uhp->uh_seq > curbuf->b_u_seq_cur)
2450 && ((dosec && val == closest)
2451 ? (step < 0
2452 ? uhp->uh_seq < closest_seq
2453 : uhp->uh_seq > closest_seq)
2454 : closest == closest_start
2455 || (val > target
2456 ? (closest > target
2457 ? val - target <= closest - target
2458 : val - target <= target - closest)
2459 : (closest > target
2460 ? target - val <= closest - target
2461 : target - val <= target - closest))))
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002462 {
2463 closest = val;
2464 closest_seq = uhp->uh_seq;
2465 }
2466 }
2467
2468 /* Quit searching when we found a match. But when searching for a
2469 * time we need to continue looking for the best uh_seq. */
2470 if (target == val && !dosec)
Bram Moolenaar730cde92010-06-27 05:18:54 +02002471 {
2472 target = uhp->uh_seq;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002473 break;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002474 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002475
2476 /* go down in the tree if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002477 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
2478 && uhp->uh_prev.ptr->uh_walk != mark)
2479 uhp = uhp->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002480
2481 /* go to alternate branch if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002482 else if (uhp->uh_alt_next.ptr != NULL
2483 && uhp->uh_alt_next.ptr->uh_walk != nomark
2484 && uhp->uh_alt_next.ptr->uh_walk != mark)
2485 uhp = uhp->uh_alt_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002486
2487 /* go up in the tree if we haven't been there and we are at the
2488 * start of alternate branches */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002489 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
2490 && uhp->uh_next.ptr->uh_walk != nomark
2491 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002492 {
2493 /* If still at the start we don't go through this change. */
2494 if (uhp == curbuf->b_u_curhead)
2495 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002496 uhp = uhp->uh_next.ptr;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002497 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002498
2499 else
2500 {
2501 /* need to backtrack; mark this node as useless */
2502 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002503 if (uhp->uh_alt_prev.ptr != NULL)
2504 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002505 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002506 uhp = uhp->uh_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002507 }
2508 }
2509
2510 if (uhp != NULL) /* found it */
2511 break;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002512
2513 if (absolute)
2514 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002515 EMSGN(_("E830: Undo number %ld not found"), step);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002516 return;
2517 }
2518
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002519 if (closest == closest_start)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002520 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002521 if (step < 0)
2522 MSG(_("Already at oldest change"));
2523 else
2524 MSG(_("Already at newest change"));
Bram Moolenaar1e607892006-03-13 22:15:53 +00002525 return;
2526 }
2527
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002528 target = closest_seq;
2529 dosec = FALSE;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002530 dofile = FALSE;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002531 if (step < 0)
2532 above = TRUE; /* stop above the header */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002533 }
2534
2535 /* If we found it: Follow the path to go to where we want to be. */
2536 if (uhp != NULL)
2537 {
2538 /*
2539 * First go up the tree as much as needed.
2540 */
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002541 while (!got_int)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002542 {
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002543 /* Do the change warning now, for the same reason as above. */
2544 change_warning(0);
2545
Bram Moolenaar1e607892006-03-13 22:15:53 +00002546 uhp = curbuf->b_u_curhead;
2547 if (uhp == NULL)
2548 uhp = curbuf->b_u_newhead;
2549 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002550 uhp = uhp->uh_next.ptr;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002551 if (uhp == NULL || uhp->uh_walk != mark
2552 || (uhp->uh_seq == target && !above))
Bram Moolenaar1e607892006-03-13 22:15:53 +00002553 break;
2554 curbuf->b_u_curhead = uhp;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002555 u_undoredo(TRUE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002556 uhp->uh_walk = nomark; /* don't go back down here */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002557 }
2558
2559 /*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002560 * And now go down the tree (redo), branching off where needed.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002561 */
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002562 while (!got_int)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002563 {
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002564 /* Do the change warning now, for the same reason as above. */
2565 change_warning(0);
2566
2567 uhp = curbuf->b_u_curhead;
2568 if (uhp == NULL)
2569 break;
2570
Bram Moolenaar89ed3df2007-01-09 19:23:12 +00002571 /* Go back to the first branch with a mark. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002572 while (uhp->uh_alt_prev.ptr != NULL
2573 && uhp->uh_alt_prev.ptr->uh_walk == mark)
2574 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaar89ed3df2007-01-09 19:23:12 +00002575
Bram Moolenaar1e607892006-03-13 22:15:53 +00002576 /* Find the last branch with a mark, that's the one. */
2577 last = uhp;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002578 while (last->uh_alt_next.ptr != NULL
2579 && last->uh_alt_next.ptr->uh_walk == mark)
2580 last = last->uh_alt_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002581 if (last != uhp)
2582 {
2583 /* Make the used branch the first entry in the list of
2584 * alternatives to make "u" and CTRL-R take this branch. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002585 while (uhp->uh_alt_prev.ptr != NULL)
2586 uhp = uhp->uh_alt_prev.ptr;
2587 if (last->uh_alt_next.ptr != NULL)
2588 last->uh_alt_next.ptr->uh_alt_prev.ptr =
2589 last->uh_alt_prev.ptr;
2590 last->uh_alt_prev.ptr->uh_alt_next.ptr = last->uh_alt_next.ptr;
2591 last->uh_alt_prev.ptr = NULL;
2592 last->uh_alt_next.ptr = uhp;
2593 uhp->uh_alt_prev.ptr = last;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002594
Bram Moolenaar8f1f6292010-05-30 16:55:22 +02002595 if (curbuf->b_u_oldhead == uhp)
2596 curbuf->b_u_oldhead = last;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002597 uhp = last;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002598 if (uhp->uh_next.ptr != NULL)
2599 uhp->uh_next.ptr->uh_prev.ptr = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002600 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002601 curbuf->b_u_curhead = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002602
2603 if (uhp->uh_walk != mark)
2604 break; /* must have reached the target */
2605
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002606 /* Stop when going backwards in time and didn't find the exact
2607 * header we were looking for. */
2608 if (uhp->uh_seq == target && above)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002609 {
2610 curbuf->b_u_seq_cur = target - 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002611 break;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002612 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002613
Bram Moolenaarca003e12006-03-17 23:19:38 +00002614 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002615
2616 /* Advance "curhead" to below the header we last used. If it
2617 * becomes NULL then we need to set "newhead" to this leaf. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002618 if (uhp->uh_prev.ptr == NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002619 curbuf->b_u_newhead = uhp;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002620 curbuf->b_u_curhead = uhp->uh_prev.ptr;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002621 did_undo = FALSE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002622
2623 if (uhp->uh_seq == target) /* found it! */
2624 break;
2625
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002626 uhp = uhp->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002627 if (uhp == NULL || uhp->uh_walk != mark)
2628 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002629 /* Need to redo more but can't find it... */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002630 EMSG2(_(e_intern2), "undo_time()");
2631 break;
2632 }
2633 }
2634 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00002635 u_undo_end(did_undo, absolute);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002636}
2637
2638/*
2639 * u_undoredo: common code for undo and redo
2640 *
2641 * The lines in the file are replaced by the lines in the entry list at
2642 * curbuf->b_u_curhead. The replaced lines in the file are saved in the entry
2643 * list for the next undo/redo.
Bram Moolenaarca003e12006-03-17 23:19:38 +00002644 *
2645 * When "undo" is TRUE we go up in the tree, when FALSE we go down.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002646 */
2647 static void
Bram Moolenaarca003e12006-03-17 23:19:38 +00002648u_undoredo(undo)
2649 int undo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002650{
2651 char_u **newarray = NULL;
2652 linenr_T oldsize;
2653 linenr_T newsize;
2654 linenr_T top, bot;
2655 linenr_T lnum;
2656 linenr_T newlnum = MAXLNUM;
2657 long i;
2658 u_entry_T *uep, *nuep;
2659 u_entry_T *newlist = NULL;
2660 int old_flags;
2661 int new_flags;
2662 pos_T namedm[NMARKS];
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002663 visualinfo_T visualinfo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002664 int empty_buffer; /* buffer became empty */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002665 u_header_T *curhead = curbuf->b_u_curhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002666
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002667#ifdef FEAT_AUTOCMD
2668 /* Don't want autocommands using the undo structures here, they are
2669 * invalid till the end. */
2670 block_autocmds();
2671#endif
2672
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002673#ifdef U_DEBUG
2674 u_check(FALSE);
2675#endif
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002676 old_flags = curhead->uh_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002677 new_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
2678 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
2679 setpcmark();
2680
2681 /*
2682 * save marks before undo/redo
2683 */
2684 mch_memmove(namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002685 visualinfo = curbuf->b_visual;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002686 curbuf->b_op_start.lnum = curbuf->b_ml.ml_line_count;
2687 curbuf->b_op_start.col = 0;
2688 curbuf->b_op_end.lnum = 0;
2689 curbuf->b_op_end.col = 0;
2690
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002691 for (uep = curhead->uh_entry; uep != NULL; uep = nuep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002692 {
2693 top = uep->ue_top;
2694 bot = uep->ue_bot;
2695 if (bot == 0)
2696 bot = curbuf->b_ml.ml_line_count + 1;
2697 if (top > curbuf->b_ml.ml_line_count || top >= bot
2698 || bot > curbuf->b_ml.ml_line_count + 1)
2699 {
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002700#ifdef FEAT_AUTOCMD
2701 unblock_autocmds();
2702#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002703 EMSG(_("E438: u_undo: line numbers wrong"));
2704 changed(); /* don't want UNCHANGED now */
2705 return;
2706 }
2707
2708 oldsize = bot - top - 1; /* number of lines before undo */
2709 newsize = uep->ue_size; /* number of lines after undo */
2710
2711 if (top < newlnum)
2712 {
2713 /* If the saved cursor is somewhere in this undo block, move it to
2714 * the remembered position. Makes "gwap" put the cursor back
2715 * where it was. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002716 lnum = curhead->uh_cursor.lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002717 if (lnum >= top && lnum <= top + newsize + 1)
2718 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002719 curwin->w_cursor = curhead->uh_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002720 newlnum = curwin->w_cursor.lnum - 1;
2721 }
2722 else
2723 {
2724 /* Use the first line that actually changed. Avoids that
2725 * undoing auto-formatting puts the cursor in the previous
2726 * line. */
2727 for (i = 0; i < newsize && i < oldsize; ++i)
2728 if (STRCMP(uep->ue_array[i], ml_get(top + 1 + i)) != 0)
2729 break;
2730 if (i == newsize && newlnum == MAXLNUM && uep->ue_next == NULL)
2731 {
2732 newlnum = top;
2733 curwin->w_cursor.lnum = newlnum + 1;
2734 }
2735 else if (i < newsize)
2736 {
2737 newlnum = top + i;
2738 curwin->w_cursor.lnum = newlnum + 1;
2739 }
2740 }
2741 }
2742
2743 empty_buffer = FALSE;
2744
2745 /* delete the lines between top and bot and save them in newarray */
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002746 if (oldsize > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002747 {
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002748 if ((newarray = (char_u **)U_ALLOC_LINE(
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002749 sizeof(char_u *) * oldsize)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002750 {
2751 do_outofmem_msg((long_u)(sizeof(char_u *) * oldsize));
2752 /*
2753 * We have messed up the entry list, repair is impossible.
2754 * we have to free the rest of the list.
2755 */
2756 while (uep != NULL)
2757 {
2758 nuep = uep->ue_next;
2759 u_freeentry(uep, uep->ue_size);
2760 uep = nuep;
2761 }
2762 break;
2763 }
2764 /* delete backwards, it goes faster in most cases */
2765 for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum)
2766 {
2767 /* what can we do when we run out of memory? */
2768 if ((newarray[i] = u_save_line(lnum)) == NULL)
2769 do_outofmem_msg((long_u)0);
2770 /* remember we deleted the last line in the buffer, and a
2771 * dummy empty line will be inserted */
2772 if (curbuf->b_ml.ml_line_count == 1)
2773 empty_buffer = TRUE;
2774 ml_delete(lnum, FALSE);
2775 }
2776 }
Bram Moolenaar8d343302005-07-12 22:46:17 +00002777 else
2778 newarray = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002779
2780 /* insert the lines in u_array between top and bot */
2781 if (newsize)
2782 {
2783 for (lnum = top, i = 0; i < newsize; ++i, ++lnum)
2784 {
2785 /*
2786 * If the file is empty, there is an empty line 1 that we
2787 * should get rid of, by replacing it with the new line
2788 */
2789 if (empty_buffer && lnum == 0)
2790 ml_replace((linenr_T)1, uep->ue_array[i], TRUE);
2791 else
2792 ml_append(lnum, uep->ue_array[i], (colnr_T)0, FALSE);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002793 vim_free(uep->ue_array[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002794 }
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002795 vim_free((char_u *)uep->ue_array);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002796 }
2797
2798 /* adjust marks */
2799 if (oldsize != newsize)
2800 {
2801 mark_adjust(top + 1, top + oldsize, (long)MAXLNUM,
2802 (long)newsize - (long)oldsize);
2803 if (curbuf->b_op_start.lnum > top + oldsize)
2804 curbuf->b_op_start.lnum += newsize - oldsize;
2805 if (curbuf->b_op_end.lnum > top + oldsize)
2806 curbuf->b_op_end.lnum += newsize - oldsize;
2807 }
2808
2809 changed_lines(top + 1, 0, bot, newsize - oldsize);
2810
2811 /* set '[ and '] mark */
2812 if (top + 1 < curbuf->b_op_start.lnum)
2813 curbuf->b_op_start.lnum = top + 1;
2814 if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum)
2815 curbuf->b_op_end.lnum = top + 1;
2816 else if (top + newsize > curbuf->b_op_end.lnum)
2817 curbuf->b_op_end.lnum = top + newsize;
2818
2819 u_newcount += newsize;
2820 u_oldcount += oldsize;
2821 uep->ue_size = oldsize;
2822 uep->ue_array = newarray;
2823 uep->ue_bot = top + newsize + 1;
2824
2825 /*
2826 * insert this entry in front of the new entry list
2827 */
2828 nuep = uep->ue_next;
2829 uep->ue_next = newlist;
2830 newlist = uep;
2831 }
2832
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002833 curhead->uh_entry = newlist;
2834 curhead->uh_flags = new_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002835 if ((old_flags & UH_EMPTYBUF) && bufempty())
2836 curbuf->b_ml.ml_flags |= ML_EMPTY;
2837 if (old_flags & UH_CHANGED)
2838 changed();
2839 else
Bram Moolenaar009b2592004-10-24 19:18:58 +00002840#ifdef FEAT_NETBEANS_INTG
2841 /* per netbeans undo rules, keep it as modified */
2842 if (!isNetbeansModified(curbuf))
2843#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002844 unchanged(curbuf, FALSE);
2845
2846 /*
2847 * restore marks from before undo/redo
2848 */
2849 for (i = 0; i < NMARKS; ++i)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002850 if (curhead->uh_namedm[i].lnum != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002851 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002852 curbuf->b_namedm[i] = curhead->uh_namedm[i];
2853 curhead->uh_namedm[i] = namedm[i];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002854 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002855 if (curhead->uh_visual.vi_start.lnum != 0)
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002856 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002857 curbuf->b_visual = curhead->uh_visual;
2858 curhead->uh_visual = visualinfo;
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002859 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002860
2861 /*
2862 * If the cursor is only off by one line, put it at the same position as
2863 * before starting the change (for the "o" command).
2864 * Otherwise the cursor should go to the first undone line.
2865 */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002866 if (curhead->uh_cursor.lnum + 1 == curwin->w_cursor.lnum
Bram Moolenaar071d4272004-06-13 20:20:40 +00002867 && curwin->w_cursor.lnum > 1)
2868 --curwin->w_cursor.lnum;
Bram Moolenaar0390ded2010-08-07 12:54:12 +02002869 if (curwin->w_cursor.lnum <= curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002870 {
Bram Moolenaar0390ded2010-08-07 12:54:12 +02002871 if (curhead->uh_cursor.lnum == curwin->w_cursor.lnum)
2872 {
2873 curwin->w_cursor.col = curhead->uh_cursor.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002874#ifdef FEAT_VIRTUALEDIT
Bram Moolenaar0390ded2010-08-07 12:54:12 +02002875 if (virtual_active() && curhead->uh_cursor_vcol >= 0)
2876 coladvance((colnr_T)curhead->uh_cursor_vcol);
2877 else
2878 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002879#endif
Bram Moolenaar0390ded2010-08-07 12:54:12 +02002880 }
2881 else
2882 beginline(BL_SOL | BL_FIX);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002883 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002884 else
2885 {
2886 /* We get here with the current cursor line being past the end (eg
2887 * after adding lines at the end of the file, and then undoing it).
2888 * check_cursor() will move the cursor to the last line. Move it to
2889 * the first column here. */
2890 curwin->w_cursor.col = 0;
2891#ifdef FEAT_VIRTUALEDIT
2892 curwin->w_cursor.coladd = 0;
2893#endif
2894 }
2895
2896 /* Make sure the cursor is on an existing line and column. */
2897 check_cursor();
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002898
2899 /* Remember where we are for "g-" and ":earlier 10s". */
2900 curbuf->b_u_seq_cur = curhead->uh_seq;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002901 if (undo)
2902 /* We are below the previous undo. However, to make ":earlier 1s"
2903 * work we compute this as being just above the just undone change. */
2904 --curbuf->b_u_seq_cur;
2905
Bram Moolenaar730cde92010-06-27 05:18:54 +02002906 /* Remember where we are for ":earlier 1f" and ":later 1f". */
2907 if (curhead->uh_save_nr != 0)
2908 {
2909 if (undo)
2910 curbuf->b_u_save_nr_cur = curhead->uh_save_nr - 1;
2911 else
2912 curbuf->b_u_save_nr_cur = curhead->uh_save_nr;
2913 }
2914
Bram Moolenaarca003e12006-03-17 23:19:38 +00002915 /* The timestamp can be the same for multiple changes, just use the one of
2916 * the undone/redone change. */
Bram Moolenaara800b422010-06-27 01:15:55 +02002917 curbuf->b_u_time_cur = curhead->uh_time;
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002918
2919#ifdef FEAT_AUTOCMD
2920 unblock_autocmds();
2921#endif
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002922#ifdef U_DEBUG
2923 u_check(FALSE);
2924#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002925}
2926
2927/*
2928 * If we deleted or added lines, report the number of less/more lines.
2929 * Otherwise, report the number of changes (this may be incorrect
2930 * in some cases, but it's better than nothing).
2931 */
2932 static void
Bram Moolenaardb552d602006-03-23 22:59:57 +00002933u_undo_end(did_undo, absolute)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002934 int did_undo; /* just did an undo */
Bram Moolenaardb552d602006-03-23 22:59:57 +00002935 int absolute; /* used ":undo N" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002936{
Bram Moolenaar89d40322006-08-29 15:30:07 +00002937 char *msgstr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002938 u_header_T *uhp;
2939 char_u msgbuf[80];
Bram Moolenaar1e607892006-03-13 22:15:53 +00002940
Bram Moolenaar071d4272004-06-13 20:20:40 +00002941#ifdef FEAT_FOLDING
2942 if ((fdo_flags & FDO_UNDO) && KeyTyped)
2943 foldOpenCursor();
2944#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +00002945
2946 if (global_busy /* no messages now, wait until global is finished */
2947 || !messaging()) /* 'lazyredraw' set, don't do messages now */
2948 return;
2949
2950 if (curbuf->b_ml.ml_flags & ML_EMPTY)
2951 --u_newcount;
2952
2953 u_oldcount -= u_newcount;
2954 if (u_oldcount == -1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002955 msgstr = N_("more line");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002956 else if (u_oldcount < 0)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002957 msgstr = N_("more lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002958 else if (u_oldcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002959 msgstr = N_("line less");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002960 else if (u_oldcount > 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002961 msgstr = N_("fewer lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002962 else
2963 {
2964 u_oldcount = u_newcount;
2965 if (u_newcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002966 msgstr = N_("change");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002967 else
Bram Moolenaar89d40322006-08-29 15:30:07 +00002968 msgstr = N_("changes");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002969 }
2970
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002971 if (curbuf->b_u_curhead != NULL)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002972 {
Bram Moolenaardb552d602006-03-23 22:59:57 +00002973 /* For ":undo N" we prefer a "after #N" message. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002974 if (absolute && curbuf->b_u_curhead->uh_next.ptr != NULL)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002975 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002976 uhp = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002977 did_undo = FALSE;
2978 }
2979 else if (did_undo)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002980 uhp = curbuf->b_u_curhead;
2981 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002982 uhp = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002983 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002984 else
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002985 uhp = curbuf->b_u_newhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002986
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002987 if (uhp == NULL)
2988 *msgbuf = NUL;
2989 else
2990 u_add_time(msgbuf, sizeof(msgbuf), uhp->uh_time);
2991
Bram Moolenaarb2c03502010-07-02 20:20:09 +02002992#ifdef FEAT_CONCEAL
2993 {
2994 win_T *wp;
2995
2996 FOR_ALL_WINDOWS(wp)
2997 {
Bram Moolenaarf5963f72010-07-23 22:10:27 +02002998 if (wp->w_buffer == curbuf && wp->w_p_cole > 0)
Bram Moolenaarb2c03502010-07-02 20:20:09 +02002999 redraw_win_later(wp, NOT_VALID);
3000 }
3001 }
3002#endif
3003
Bram Moolenaar433f7c82006-03-21 21:29:36 +00003004 smsg((char_u *)_("%ld %s; %s #%ld %s"),
Bram Moolenaarca003e12006-03-17 23:19:38 +00003005 u_oldcount < 0 ? -u_oldcount : u_oldcount,
Bram Moolenaar89d40322006-08-29 15:30:07 +00003006 _(msgstr),
Bram Moolenaar433f7c82006-03-21 21:29:36 +00003007 did_undo ? _("before") : _("after"),
3008 uhp == NULL ? 0L : uhp->uh_seq,
3009 msgbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003010}
3011
3012/*
3013 * u_sync: stop adding to the current entry list
3014 */
3015 void
Bram Moolenaar779b74b2006-04-10 14:55:34 +00003016u_sync(force)
3017 int force; /* Also sync when no_u_sync is set. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003018{
Bram Moolenaar779b74b2006-04-10 14:55:34 +00003019 /* Skip it when already synced or syncing is disabled. */
3020 if (curbuf->b_u_synced || (!force && no_u_sync > 0))
3021 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003022#if defined(FEAT_XIM) && defined(FEAT_GUI_GTK)
3023 if (im_is_preediting())
3024 return; /* XIM is busy, don't break an undo sequence */
3025#endif
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +01003026 if (get_undolevel() < 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003027 curbuf->b_u_synced = TRUE; /* no entries, nothing to do */
3028 else
3029 {
3030 u_getbot(); /* compute ue_bot of previous u_save */
3031 curbuf->b_u_curhead = NULL;
3032 }
3033}
3034
3035/*
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003036 * ":undolist": List the leafs of the undo tree
3037 */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003038 void
3039ex_undolist(eap)
Bram Moolenaarfff2bee2010-05-15 13:56:02 +02003040 exarg_T *eap UNUSED;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003041{
3042 garray_T ga;
3043 u_header_T *uhp;
3044 int mark;
3045 int nomark;
3046 int changes = 1;
3047 int i;
3048
3049 /*
3050 * 1: walk the tree to find all leafs, put the info in "ga".
3051 * 2: sort the lines
3052 * 3: display the list
3053 */
3054 mark = ++lastmark;
3055 nomark = ++lastmark;
3056 ga_init2(&ga, (int)sizeof(char *), 20);
3057
3058 uhp = curbuf->b_u_oldhead;
3059 while (uhp != NULL)
3060 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003061 if (uhp->uh_prev.ptr == NULL && uhp->uh_walk != nomark
Bram Moolenaarca003e12006-03-17 23:19:38 +00003062 && uhp->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003063 {
3064 if (ga_grow(&ga, 1) == FAIL)
3065 break;
3066 vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7ld ",
3067 uhp->uh_seq, changes);
3068 u_add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff),
3069 uhp->uh_time);
Bram Moolenaara800b422010-06-27 01:15:55 +02003070 if (uhp->uh_save_nr > 0)
3071 {
Bram Moolenaardba01a02010-11-03 19:32:42 +01003072 while (STRLEN(IObuff) < 33)
Bram Moolenaara800b422010-06-27 01:15:55 +02003073 STRCAT(IObuff, " ");
3074 vim_snprintf_add((char *)IObuff, IOSIZE,
3075 " %3ld", uhp->uh_save_nr);
3076 }
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003077 ((char_u **)(ga.ga_data))[ga.ga_len++] = vim_strsave(IObuff);
3078 }
3079
3080 uhp->uh_walk = mark;
3081
3082 /* go down in the tree if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003083 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
3084 && uhp->uh_prev.ptr->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003085 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003086 uhp = uhp->uh_prev.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003087 ++changes;
3088 }
3089
3090 /* go to alternate branch if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003091 else if (uhp->uh_alt_next.ptr != NULL
3092 && uhp->uh_alt_next.ptr->uh_walk != nomark
3093 && uhp->uh_alt_next.ptr->uh_walk != mark)
3094 uhp = uhp->uh_alt_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003095
3096 /* go up in the tree if we haven't been there and we are at the
3097 * start of alternate branches */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003098 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
3099 && uhp->uh_next.ptr->uh_walk != nomark
3100 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003101 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003102 uhp = uhp->uh_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003103 --changes;
3104 }
3105
3106 else
3107 {
3108 /* need to backtrack; mark this node as done */
3109 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003110 if (uhp->uh_alt_prev.ptr != NULL)
3111 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003112 else
3113 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003114 uhp = uhp->uh_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003115 --changes;
3116 }
3117 }
3118 }
3119
3120 if (ga.ga_len == 0)
3121 MSG(_("Nothing to undo"));
3122 else
3123 {
3124 sort_strings((char_u **)ga.ga_data, ga.ga_len);
3125
3126 msg_start();
Bram Moolenaardba01a02010-11-03 19:32:42 +01003127 msg_puts_attr((char_u *)_("number changes when saved"),
Bram Moolenaara800b422010-06-27 01:15:55 +02003128 hl_attr(HLF_T));
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003129 for (i = 0; i < ga.ga_len && !got_int; ++i)
3130 {
3131 msg_putchar('\n');
3132 if (got_int)
3133 break;
3134 msg_puts(((char_u **)ga.ga_data)[i]);
3135 }
3136 msg_end();
3137
3138 ga_clear_strings(&ga);
3139 }
3140}
3141
3142/*
3143 * Put the timestamp of an undo header in "buf[buflen]" in a nice format.
3144 */
3145 static void
3146u_add_time(buf, buflen, tt)
3147 char_u *buf;
3148 size_t buflen;
3149 time_t tt;
3150{
3151#ifdef HAVE_STRFTIME
3152 struct tm *curtime;
3153
3154 if (time(NULL) - tt >= 100)
3155 {
3156 curtime = localtime(&tt);
Bram Moolenaardba01a02010-11-03 19:32:42 +01003157 if (time(NULL) - tt < (60L * 60L * 12L))
3158 /* within 12 hours */
3159 (void)strftime((char *)buf, buflen, "%H:%M:%S", curtime);
Bram Moolenaardba01a02010-11-03 19:32:42 +01003160 else
Bram Moolenaarb9ce83e2012-08-23 12:59:02 +02003161 /* longer ago */
Bram Moolenaar5e3d6ca2011-01-22 21:25:11 +01003162 (void)strftime((char *)buf, buflen, "%Y/%m/%d %H:%M:%S", curtime);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003163 }
3164 else
3165#endif
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003166 vim_snprintf((char *)buf, buflen, _("%ld seconds ago"),
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003167 (long)(time(NULL) - tt));
3168}
3169
3170/*
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003171 * ":undojoin": continue adding to the last entry list
3172 */
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003173 void
3174ex_undojoin(eap)
Bram Moolenaarfff2bee2010-05-15 13:56:02 +02003175 exarg_T *eap UNUSED;
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003176{
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003177 if (curbuf->b_u_newhead == NULL)
3178 return; /* nothing changed before */
Bram Moolenaar57657d82006-04-21 22:12:41 +00003179 if (curbuf->b_u_curhead != NULL)
3180 {
3181 EMSG(_("E790: undojoin is not allowed after undo"));
3182 return;
3183 }
3184 if (!curbuf->b_u_synced)
3185 return; /* already unsynced */
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +01003186 if (get_undolevel() < 0)
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003187 return; /* no entries, nothing to do */
3188 else
3189 {
3190 /* Go back to the last entry */
3191 curbuf->b_u_curhead = curbuf->b_u_newhead;
3192 curbuf->b_u_synced = FALSE; /* no entries, nothing to do */
3193 }
3194}
3195
3196/*
Bram Moolenaar59f931e2010-07-24 20:27:03 +02003197 * Called after writing or reloading the file and setting b_changed to FALSE.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003198 * Now an undo means that the buffer is modified.
3199 */
3200 void
3201u_unchanged(buf)
3202 buf_T *buf;
3203{
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003204 u_unch_branch(buf->b_u_oldhead);
3205 buf->b_did_warn = FALSE;
3206}
3207
Bram Moolenaar730cde92010-06-27 05:18:54 +02003208/*
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003209 * After reloading a buffer which was saved for 'undoreload': Find the first
3210 * line that was changed and set the cursor there.
3211 */
3212 void
3213u_find_first_changed()
3214{
3215 u_header_T *uhp = curbuf->b_u_newhead;
3216 u_entry_T *uep;
3217 linenr_T lnum;
3218
3219 if (curbuf->b_u_curhead != NULL || uhp == NULL)
3220 return; /* undid something in an autocmd? */
3221
3222 /* Check that the last undo block was for the whole file. */
3223 uep = uhp->uh_entry;
3224 if (uep->ue_top != 0 || uep->ue_bot != 0)
3225 return;
3226
3227 for (lnum = 1; lnum < curbuf->b_ml.ml_line_count
3228 && lnum <= uep->ue_size; ++lnum)
3229 if (STRCMP(ml_get_buf(curbuf, lnum, FALSE),
3230 uep->ue_array[lnum - 1]) != 0)
3231 {
3232 clearpos(&(uhp->uh_cursor));
3233 uhp->uh_cursor.lnum = lnum;
3234 return;
3235 }
3236 if (curbuf->b_ml.ml_line_count != uep->ue_size)
3237 {
3238 /* lines added or deleted at the end, put the cursor there */
3239 clearpos(&(uhp->uh_cursor));
3240 uhp->uh_cursor.lnum = lnum;
3241 }
3242}
3243
3244/*
Bram Moolenaar730cde92010-06-27 05:18:54 +02003245 * Increase the write count, store it in the last undo header, what would be
3246 * used for "u".
3247 */
3248 void
3249u_update_save_nr(buf)
3250 buf_T *buf;
3251{
3252 u_header_T *uhp;
3253
3254 ++buf->b_u_save_nr_last;
3255 buf->b_u_save_nr_cur = buf->b_u_save_nr_last;
3256 uhp = buf->b_u_curhead;
3257 if (uhp != NULL)
3258 uhp = uhp->uh_next.ptr;
3259 else
3260 uhp = buf->b_u_newhead;
3261 if (uhp != NULL)
3262 uhp->uh_save_nr = buf->b_u_save_nr_last;
3263}
3264
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003265 static void
3266u_unch_branch(uhp)
3267 u_header_T *uhp;
3268{
Bram Moolenaar1e607892006-03-13 22:15:53 +00003269 u_header_T *uh;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003270
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003271 for (uh = uhp; uh != NULL; uh = uh->uh_prev.ptr)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003272 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003273 uh->uh_flags |= UH_CHANGED;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003274 if (uh->uh_alt_next.ptr != NULL)
3275 u_unch_branch(uh->uh_alt_next.ptr); /* recursive */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003276 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003277}
3278
3279/*
3280 * Get pointer to last added entry.
3281 * If it's not valid, give an error message and return NULL.
3282 */
3283 static u_entry_T *
3284u_get_headentry()
3285{
3286 if (curbuf->b_u_newhead == NULL || curbuf->b_u_newhead->uh_entry == NULL)
3287 {
3288 EMSG(_("E439: undo list corrupt"));
3289 return NULL;
3290 }
3291 return curbuf->b_u_newhead->uh_entry;
3292}
3293
3294/*
3295 * u_getbot(): compute the line number of the previous u_save
3296 * It is called only when b_u_synced is FALSE.
3297 */
3298 static void
3299u_getbot()
3300{
3301 u_entry_T *uep;
3302 linenr_T extra;
3303
3304 uep = u_get_headentry(); /* check for corrupt undo list */
3305 if (uep == NULL)
3306 return;
3307
3308 uep = curbuf->b_u_newhead->uh_getbot_entry;
3309 if (uep != NULL)
3310 {
3311 /*
3312 * the new ue_bot is computed from the number of lines that has been
3313 * inserted (0 - deleted) since calling u_save. This is equal to the
3314 * old line count subtracted from the current line count.
3315 */
3316 extra = curbuf->b_ml.ml_line_count - uep->ue_lcount;
3317 uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra;
3318 if (uep->ue_bot < 1 || uep->ue_bot > curbuf->b_ml.ml_line_count)
3319 {
3320 EMSG(_("E440: undo line missing"));
3321 uep->ue_bot = uep->ue_top + 1; /* assume all lines deleted, will
3322 * get all the old lines back
3323 * without deleting the current
3324 * ones */
3325 }
3326
3327 curbuf->b_u_newhead->uh_getbot_entry = NULL;
3328 }
3329
3330 curbuf->b_u_synced = TRUE;
3331}
3332
3333/*
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003334 * Free one header "uhp" and its entry list and adjust the pointers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003335 */
3336 static void
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003337u_freeheader(buf, uhp, uhpp)
Bram Moolenaar26a60b42005-02-22 08:49:11 +00003338 buf_T *buf;
Bram Moolenaar1e607892006-03-13 22:15:53 +00003339 u_header_T *uhp;
3340 u_header_T **uhpp; /* if not NULL reset when freeing this header */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003341{
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003342 u_header_T *uhap;
3343
Bram Moolenaar1e607892006-03-13 22:15:53 +00003344 /* When there is an alternate redo list free that branch completely,
3345 * because we can never go there. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003346 if (uhp->uh_alt_next.ptr != NULL)
3347 u_freebranch(buf, uhp->uh_alt_next.ptr, uhpp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003348
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003349 if (uhp->uh_alt_prev.ptr != NULL)
3350 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003351
Bram Moolenaar1e607892006-03-13 22:15:53 +00003352 /* Update the links in the list to remove the header. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003353 if (uhp->uh_next.ptr == NULL)
3354 buf->b_u_oldhead = uhp->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003355 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003356 uhp->uh_next.ptr->uh_prev.ptr = uhp->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003357
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003358 if (uhp->uh_prev.ptr == NULL)
3359 buf->b_u_newhead = uhp->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003360 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003361 for (uhap = uhp->uh_prev.ptr; uhap != NULL;
3362 uhap = uhap->uh_alt_next.ptr)
3363 uhap->uh_next.ptr = uhp->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003364
Bram Moolenaar1e607892006-03-13 22:15:53 +00003365 u_freeentries(buf, uhp, uhpp);
3366}
3367
3368/*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003369 * Free an alternate branch and any following alternate branches.
Bram Moolenaar1e607892006-03-13 22:15:53 +00003370 */
3371 static void
3372u_freebranch(buf, uhp, uhpp)
3373 buf_T *buf;
3374 u_header_T *uhp;
3375 u_header_T **uhpp; /* if not NULL reset when freeing this header */
3376{
3377 u_header_T *tofree, *next;
3378
Bram Moolenaar07d06772007-11-10 21:51:15 +00003379 /* If this is the top branch we may need to use u_freeheader() to update
3380 * all the pointers. */
3381 if (uhp == buf->b_u_oldhead)
3382 {
Bram Moolenaaraa887322013-11-07 03:04:11 +01003383 while (buf->b_u_oldhead != NULL)
3384 u_freeheader(buf, buf->b_u_oldhead, uhpp);
Bram Moolenaar07d06772007-11-10 21:51:15 +00003385 return;
3386 }
3387
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003388 if (uhp->uh_alt_prev.ptr != NULL)
3389 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +00003390
3391 next = uhp;
3392 while (next != NULL)
3393 {
3394 tofree = next;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003395 if (tofree->uh_alt_next.ptr != NULL)
3396 u_freebranch(buf, tofree->uh_alt_next.ptr, uhpp); /* recursive */
3397 next = tofree->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00003398 u_freeentries(buf, tofree, uhpp);
3399 }
3400}
3401
3402/*
3403 * Free all the undo entries for one header and the header itself.
3404 * This means that "uhp" is invalid when returning.
3405 */
3406 static void
3407u_freeentries(buf, uhp, uhpp)
3408 buf_T *buf;
3409 u_header_T *uhp;
3410 u_header_T **uhpp; /* if not NULL reset when freeing this header */
3411{
3412 u_entry_T *uep, *nuep;
3413
3414 /* Check for pointers to the header that become invalid now. */
3415 if (buf->b_u_curhead == uhp)
3416 buf->b_u_curhead = NULL;
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003417 if (buf->b_u_newhead == uhp)
3418 buf->b_u_newhead = NULL; /* freeing the newest entry */
Bram Moolenaar1e607892006-03-13 22:15:53 +00003419 if (uhpp != NULL && uhp == *uhpp)
3420 *uhpp = NULL;
3421
3422 for (uep = uhp->uh_entry; uep != NULL; uep = nuep)
3423 {
3424 nuep = uep->ue_next;
3425 u_freeentry(uep, uep->ue_size);
3426 }
3427
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003428#ifdef U_DEBUG
3429 uhp->uh_magic = 0;
3430#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003431 vim_free((char_u *)uhp);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00003432 --buf->b_u_numhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003433}
3434
3435/*
3436 * free entry 'uep' and 'n' lines in uep->ue_array[]
3437 */
3438 static void
3439u_freeentry(uep, n)
3440 u_entry_T *uep;
3441 long n;
3442{
Bram Moolenaar8d343302005-07-12 22:46:17 +00003443 while (n > 0)
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003444 vim_free(uep->ue_array[--n]);
3445 vim_free((char_u *)uep->ue_array);
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003446#ifdef U_DEBUG
3447 uep->ue_magic = 0;
3448#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003449 vim_free((char_u *)uep);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003450}
3451
3452/*
3453 * invalidate the undo buffer; called when storage has already been released
3454 */
3455 void
3456u_clearall(buf)
3457 buf_T *buf;
3458{
3459 buf->b_u_newhead = buf->b_u_oldhead = buf->b_u_curhead = NULL;
3460 buf->b_u_synced = TRUE;
3461 buf->b_u_numhead = 0;
3462 buf->b_u_line_ptr = NULL;
3463 buf->b_u_line_lnum = 0;
3464}
3465
3466/*
3467 * save the line "lnum" for the "U" command
3468 */
3469 void
3470u_saveline(lnum)
3471 linenr_T lnum;
3472{
3473 if (lnum == curbuf->b_u_line_lnum) /* line is already saved */
3474 return;
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00003475 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) /* should never happen */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003476 return;
3477 u_clearline();
3478 curbuf->b_u_line_lnum = lnum;
3479 if (curwin->w_cursor.lnum == lnum)
3480 curbuf->b_u_line_colnr = curwin->w_cursor.col;
3481 else
3482 curbuf->b_u_line_colnr = 0;
3483 if ((curbuf->b_u_line_ptr = u_save_line(lnum)) == NULL)
3484 do_outofmem_msg((long_u)0);
3485}
3486
3487/*
3488 * clear the line saved for the "U" command
3489 * (this is used externally for crossing a line while in insert mode)
3490 */
3491 void
3492u_clearline()
3493{
3494 if (curbuf->b_u_line_ptr != NULL)
3495 {
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003496 vim_free(curbuf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003497 curbuf->b_u_line_ptr = NULL;
3498 curbuf->b_u_line_lnum = 0;
3499 }
3500}
3501
3502/*
3503 * Implementation of the "U" command.
3504 * Differentiation from vi: "U" can be undone with the next "U".
3505 * We also allow the cursor to be in another line.
Bram Moolenaard04b7502010-07-08 22:27:55 +02003506 * Careful: may trigger autocommands that reload the buffer.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003507 */
3508 void
3509u_undoline()
3510{
3511 colnr_T t;
3512 char_u *oldp;
3513
3514 if (undo_off)
3515 return;
3516
Bram Moolenaare3300c82008-02-13 14:21:38 +00003517 if (curbuf->b_u_line_ptr == NULL
3518 || curbuf->b_u_line_lnum > curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003519 {
3520 beep_flush();
3521 return;
3522 }
Bram Moolenaare3300c82008-02-13 14:21:38 +00003523
3524 /* first save the line for the 'u' command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003525 if (u_savecommon(curbuf->b_u_line_lnum - 1,
Bram Moolenaar59f931e2010-07-24 20:27:03 +02003526 curbuf->b_u_line_lnum + 1, (linenr_T)0, FALSE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003527 return;
3528 oldp = u_save_line(curbuf->b_u_line_lnum);
3529 if (oldp == NULL)
3530 {
3531 do_outofmem_msg((long_u)0);
3532 return;
3533 }
3534 ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, TRUE);
3535 changed_bytes(curbuf->b_u_line_lnum, 0);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003536 vim_free(curbuf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003537 curbuf->b_u_line_ptr = oldp;
3538
3539 t = curbuf->b_u_line_colnr;
3540 if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum)
3541 curbuf->b_u_line_colnr = curwin->w_cursor.col;
3542 curwin->w_cursor.col = t;
3543 curwin->w_cursor.lnum = curbuf->b_u_line_lnum;
Bram Moolenaare3300c82008-02-13 14:21:38 +00003544 check_cursor_col();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003545}
3546
3547/*
Bram Moolenaar26a60b42005-02-22 08:49:11 +00003548 * Free all allocated memory blocks for the buffer 'buf'.
3549 */
3550 void
3551u_blockfree(buf)
3552 buf_T *buf;
3553{
Bram Moolenaar1e607892006-03-13 22:15:53 +00003554 while (buf->b_u_oldhead != NULL)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003555 u_freeheader(buf, buf->b_u_oldhead, NULL);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003556 vim_free(buf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003557}
3558
3559/*
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003560 * u_save_line(): allocate memory and copy line 'lnum' into it.
3561 * Returns NULL when out of memory.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003562 */
3563 static char_u *
3564u_save_line(lnum)
3565 linenr_T lnum;
3566{
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003567 return vim_strsave(ml_get(lnum));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003568}
3569
3570/*
3571 * Check if the 'modified' flag is set, or 'ff' has changed (only need to
3572 * check the first character, because it can only be "dos", "unix" or "mac").
3573 * "nofile" and "scratch" type buffers are considered to always be unchanged.
3574 */
3575 int
3576bufIsChanged(buf)
3577 buf_T *buf;
3578{
3579 return
3580#ifdef FEAT_QUICKFIX
3581 !bt_dontwrite(buf) &&
3582#endif
Bram Moolenaar164c60f2011-01-22 00:11:50 +01003583 (buf->b_changed || file_ff_differs(buf, TRUE));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003584}
3585
3586 int
3587curbufIsChanged()
3588{
3589 return
3590#ifdef FEAT_QUICKFIX
3591 !bt_dontwrite(curbuf) &&
3592#endif
Bram Moolenaar164c60f2011-01-22 00:11:50 +01003593 (curbuf->b_changed || file_ff_differs(curbuf, TRUE));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003594}
Bram Moolenaara800b422010-06-27 01:15:55 +02003595
3596#if defined(FEAT_EVAL) || defined(PROTO)
3597/*
3598 * For undotree(): Append the list of undo blocks at "first_uhp" to "list".
3599 * Recursive.
3600 */
3601 void
3602u_eval_tree(first_uhp, list)
3603 u_header_T *first_uhp;
3604 list_T *list;
3605{
3606 u_header_T *uhp = first_uhp;
3607 dict_T *dict;
3608
3609 while (uhp != NULL)
3610 {
3611 dict = dict_alloc();
3612 if (dict == NULL)
3613 return;
3614 dict_add_nr_str(dict, "seq", uhp->uh_seq, NULL);
Bram Moolenaarb2c03502010-07-02 20:20:09 +02003615 dict_add_nr_str(dict, "time", (long)uhp->uh_time, NULL);
Bram Moolenaara800b422010-06-27 01:15:55 +02003616 if (uhp == curbuf->b_u_newhead)
3617 dict_add_nr_str(dict, "newhead", 1, NULL);
3618 if (uhp == curbuf->b_u_curhead)
3619 dict_add_nr_str(dict, "curhead", 1, NULL);
3620 if (uhp->uh_save_nr > 0)
3621 dict_add_nr_str(dict, "save", uhp->uh_save_nr, NULL);
3622
3623 if (uhp->uh_alt_next.ptr != NULL)
3624 {
3625 list_T *alt_list = list_alloc();
3626
3627 if (alt_list != NULL)
3628 {
3629 /* Recursive call to add alternate undo tree. */
3630 u_eval_tree(uhp->uh_alt_next.ptr, alt_list);
3631 dict_add_list(dict, "alt", alt_list);
3632 }
3633 }
3634
3635 list_append_dict(list, dict);
3636 uhp = uhp->uh_prev.ptr;
3637 }
3638}
3639#endif