blob: 034d16c14eb0d3a99805b539595e011712fc8862 [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);
Bram Moolenaar35169282014-09-11 22:50:09 +02001022 n = ((unsigned)buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3];
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001023 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 Moolenaarf65aad52015-02-17 13:43:40 +01002850 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002851 if (curhead->uh_namedm[i].lnum != 0)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002852 curbuf->b_namedm[i] = curhead->uh_namedm[i];
Bram Moolenaarf65aad52015-02-17 13:43:40 +01002853 if (namedm[i].lnum != 0)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002854 curhead->uh_namedm[i] = namedm[i];
Bram Moolenaarf65aad52015-02-17 13:43:40 +01002855 else
2856 curhead->uh_namedm[i].lnum = 0;
2857 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002858 if (curhead->uh_visual.vi_start.lnum != 0)
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002859 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002860 curbuf->b_visual = curhead->uh_visual;
2861 curhead->uh_visual = visualinfo;
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002862 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002863
2864 /*
2865 * If the cursor is only off by one line, put it at the same position as
2866 * before starting the change (for the "o" command).
2867 * Otherwise the cursor should go to the first undone line.
2868 */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002869 if (curhead->uh_cursor.lnum + 1 == curwin->w_cursor.lnum
Bram Moolenaar071d4272004-06-13 20:20:40 +00002870 && curwin->w_cursor.lnum > 1)
2871 --curwin->w_cursor.lnum;
Bram Moolenaar0390ded2010-08-07 12:54:12 +02002872 if (curwin->w_cursor.lnum <= curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002873 {
Bram Moolenaar0390ded2010-08-07 12:54:12 +02002874 if (curhead->uh_cursor.lnum == curwin->w_cursor.lnum)
2875 {
2876 curwin->w_cursor.col = curhead->uh_cursor.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002877#ifdef FEAT_VIRTUALEDIT
Bram Moolenaar0390ded2010-08-07 12:54:12 +02002878 if (virtual_active() && curhead->uh_cursor_vcol >= 0)
2879 coladvance((colnr_T)curhead->uh_cursor_vcol);
2880 else
2881 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002882#endif
Bram Moolenaar0390ded2010-08-07 12:54:12 +02002883 }
2884 else
2885 beginline(BL_SOL | BL_FIX);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002886 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002887 else
2888 {
2889 /* We get here with the current cursor line being past the end (eg
2890 * after adding lines at the end of the file, and then undoing it).
2891 * check_cursor() will move the cursor to the last line. Move it to
2892 * the first column here. */
2893 curwin->w_cursor.col = 0;
2894#ifdef FEAT_VIRTUALEDIT
2895 curwin->w_cursor.coladd = 0;
2896#endif
2897 }
2898
2899 /* Make sure the cursor is on an existing line and column. */
2900 check_cursor();
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002901
2902 /* Remember where we are for "g-" and ":earlier 10s". */
2903 curbuf->b_u_seq_cur = curhead->uh_seq;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002904 if (undo)
2905 /* We are below the previous undo. However, to make ":earlier 1s"
2906 * work we compute this as being just above the just undone change. */
2907 --curbuf->b_u_seq_cur;
2908
Bram Moolenaar730cde92010-06-27 05:18:54 +02002909 /* Remember where we are for ":earlier 1f" and ":later 1f". */
2910 if (curhead->uh_save_nr != 0)
2911 {
2912 if (undo)
2913 curbuf->b_u_save_nr_cur = curhead->uh_save_nr - 1;
2914 else
2915 curbuf->b_u_save_nr_cur = curhead->uh_save_nr;
2916 }
2917
Bram Moolenaarca003e12006-03-17 23:19:38 +00002918 /* The timestamp can be the same for multiple changes, just use the one of
2919 * the undone/redone change. */
Bram Moolenaara800b422010-06-27 01:15:55 +02002920 curbuf->b_u_time_cur = curhead->uh_time;
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002921
2922#ifdef FEAT_AUTOCMD
2923 unblock_autocmds();
2924#endif
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002925#ifdef U_DEBUG
2926 u_check(FALSE);
2927#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002928}
2929
2930/*
2931 * If we deleted or added lines, report the number of less/more lines.
2932 * Otherwise, report the number of changes (this may be incorrect
2933 * in some cases, but it's better than nothing).
2934 */
2935 static void
Bram Moolenaardb552d602006-03-23 22:59:57 +00002936u_undo_end(did_undo, absolute)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002937 int did_undo; /* just did an undo */
Bram Moolenaardb552d602006-03-23 22:59:57 +00002938 int absolute; /* used ":undo N" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002939{
Bram Moolenaar89d40322006-08-29 15:30:07 +00002940 char *msgstr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002941 u_header_T *uhp;
2942 char_u msgbuf[80];
Bram Moolenaar1e607892006-03-13 22:15:53 +00002943
Bram Moolenaar071d4272004-06-13 20:20:40 +00002944#ifdef FEAT_FOLDING
2945 if ((fdo_flags & FDO_UNDO) && KeyTyped)
2946 foldOpenCursor();
2947#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +00002948
2949 if (global_busy /* no messages now, wait until global is finished */
2950 || !messaging()) /* 'lazyredraw' set, don't do messages now */
2951 return;
2952
2953 if (curbuf->b_ml.ml_flags & ML_EMPTY)
2954 --u_newcount;
2955
2956 u_oldcount -= u_newcount;
2957 if (u_oldcount == -1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002958 msgstr = N_("more line");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002959 else if (u_oldcount < 0)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002960 msgstr = N_("more lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002961 else if (u_oldcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002962 msgstr = N_("line less");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002963 else if (u_oldcount > 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002964 msgstr = N_("fewer lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002965 else
2966 {
2967 u_oldcount = u_newcount;
2968 if (u_newcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002969 msgstr = N_("change");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002970 else
Bram Moolenaar89d40322006-08-29 15:30:07 +00002971 msgstr = N_("changes");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002972 }
2973
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002974 if (curbuf->b_u_curhead != NULL)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002975 {
Bram Moolenaardb552d602006-03-23 22:59:57 +00002976 /* For ":undo N" we prefer a "after #N" message. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002977 if (absolute && curbuf->b_u_curhead->uh_next.ptr != NULL)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002978 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002979 uhp = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002980 did_undo = FALSE;
2981 }
2982 else if (did_undo)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002983 uhp = curbuf->b_u_curhead;
2984 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002985 uhp = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002986 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002987 else
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002988 uhp = curbuf->b_u_newhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002989
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002990 if (uhp == NULL)
2991 *msgbuf = NUL;
2992 else
2993 u_add_time(msgbuf, sizeof(msgbuf), uhp->uh_time);
2994
Bram Moolenaarb2c03502010-07-02 20:20:09 +02002995#ifdef FEAT_CONCEAL
2996 {
2997 win_T *wp;
2998
2999 FOR_ALL_WINDOWS(wp)
3000 {
Bram Moolenaarf5963f72010-07-23 22:10:27 +02003001 if (wp->w_buffer == curbuf && wp->w_p_cole > 0)
Bram Moolenaarb2c03502010-07-02 20:20:09 +02003002 redraw_win_later(wp, NOT_VALID);
3003 }
3004 }
3005#endif
3006
Bram Moolenaar433f7c82006-03-21 21:29:36 +00003007 smsg((char_u *)_("%ld %s; %s #%ld %s"),
Bram Moolenaarca003e12006-03-17 23:19:38 +00003008 u_oldcount < 0 ? -u_oldcount : u_oldcount,
Bram Moolenaar89d40322006-08-29 15:30:07 +00003009 _(msgstr),
Bram Moolenaar433f7c82006-03-21 21:29:36 +00003010 did_undo ? _("before") : _("after"),
3011 uhp == NULL ? 0L : uhp->uh_seq,
3012 msgbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003013}
3014
3015/*
3016 * u_sync: stop adding to the current entry list
3017 */
3018 void
Bram Moolenaar779b74b2006-04-10 14:55:34 +00003019u_sync(force)
3020 int force; /* Also sync when no_u_sync is set. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003021{
Bram Moolenaar779b74b2006-04-10 14:55:34 +00003022 /* Skip it when already synced or syncing is disabled. */
3023 if (curbuf->b_u_synced || (!force && no_u_sync > 0))
3024 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003025#if defined(FEAT_XIM) && defined(FEAT_GUI_GTK)
3026 if (im_is_preediting())
3027 return; /* XIM is busy, don't break an undo sequence */
3028#endif
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +01003029 if (get_undolevel() < 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003030 curbuf->b_u_synced = TRUE; /* no entries, nothing to do */
3031 else
3032 {
3033 u_getbot(); /* compute ue_bot of previous u_save */
3034 curbuf->b_u_curhead = NULL;
3035 }
3036}
3037
3038/*
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003039 * ":undolist": List the leafs of the undo tree
3040 */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003041 void
3042ex_undolist(eap)
Bram Moolenaarfff2bee2010-05-15 13:56:02 +02003043 exarg_T *eap UNUSED;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003044{
3045 garray_T ga;
3046 u_header_T *uhp;
3047 int mark;
3048 int nomark;
3049 int changes = 1;
3050 int i;
3051
3052 /*
3053 * 1: walk the tree to find all leafs, put the info in "ga".
3054 * 2: sort the lines
3055 * 3: display the list
3056 */
3057 mark = ++lastmark;
3058 nomark = ++lastmark;
3059 ga_init2(&ga, (int)sizeof(char *), 20);
3060
3061 uhp = curbuf->b_u_oldhead;
3062 while (uhp != NULL)
3063 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003064 if (uhp->uh_prev.ptr == NULL && uhp->uh_walk != nomark
Bram Moolenaarca003e12006-03-17 23:19:38 +00003065 && uhp->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003066 {
3067 if (ga_grow(&ga, 1) == FAIL)
3068 break;
3069 vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7ld ",
3070 uhp->uh_seq, changes);
3071 u_add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff),
3072 uhp->uh_time);
Bram Moolenaara800b422010-06-27 01:15:55 +02003073 if (uhp->uh_save_nr > 0)
3074 {
Bram Moolenaardba01a02010-11-03 19:32:42 +01003075 while (STRLEN(IObuff) < 33)
Bram Moolenaara800b422010-06-27 01:15:55 +02003076 STRCAT(IObuff, " ");
3077 vim_snprintf_add((char *)IObuff, IOSIZE,
3078 " %3ld", uhp->uh_save_nr);
3079 }
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003080 ((char_u **)(ga.ga_data))[ga.ga_len++] = vim_strsave(IObuff);
3081 }
3082
3083 uhp->uh_walk = mark;
3084
3085 /* go down in the tree if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003086 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
3087 && uhp->uh_prev.ptr->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003088 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003089 uhp = uhp->uh_prev.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003090 ++changes;
3091 }
3092
3093 /* go to alternate branch if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003094 else if (uhp->uh_alt_next.ptr != NULL
3095 && uhp->uh_alt_next.ptr->uh_walk != nomark
3096 && uhp->uh_alt_next.ptr->uh_walk != mark)
3097 uhp = uhp->uh_alt_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003098
3099 /* go up in the tree if we haven't been there and we are at the
3100 * start of alternate branches */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003101 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
3102 && uhp->uh_next.ptr->uh_walk != nomark
3103 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003104 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003105 uhp = uhp->uh_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003106 --changes;
3107 }
3108
3109 else
3110 {
3111 /* need to backtrack; mark this node as done */
3112 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003113 if (uhp->uh_alt_prev.ptr != NULL)
3114 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003115 else
3116 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003117 uhp = uhp->uh_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003118 --changes;
3119 }
3120 }
3121 }
3122
3123 if (ga.ga_len == 0)
3124 MSG(_("Nothing to undo"));
3125 else
3126 {
3127 sort_strings((char_u **)ga.ga_data, ga.ga_len);
3128
3129 msg_start();
Bram Moolenaardba01a02010-11-03 19:32:42 +01003130 msg_puts_attr((char_u *)_("number changes when saved"),
Bram Moolenaara800b422010-06-27 01:15:55 +02003131 hl_attr(HLF_T));
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003132 for (i = 0; i < ga.ga_len && !got_int; ++i)
3133 {
3134 msg_putchar('\n');
3135 if (got_int)
3136 break;
3137 msg_puts(((char_u **)ga.ga_data)[i]);
3138 }
3139 msg_end();
3140
3141 ga_clear_strings(&ga);
3142 }
3143}
3144
3145/*
3146 * Put the timestamp of an undo header in "buf[buflen]" in a nice format.
3147 */
3148 static void
3149u_add_time(buf, buflen, tt)
3150 char_u *buf;
3151 size_t buflen;
3152 time_t tt;
3153{
3154#ifdef HAVE_STRFTIME
3155 struct tm *curtime;
3156
3157 if (time(NULL) - tt >= 100)
3158 {
3159 curtime = localtime(&tt);
Bram Moolenaardba01a02010-11-03 19:32:42 +01003160 if (time(NULL) - tt < (60L * 60L * 12L))
3161 /* within 12 hours */
3162 (void)strftime((char *)buf, buflen, "%H:%M:%S", curtime);
Bram Moolenaardba01a02010-11-03 19:32:42 +01003163 else
Bram Moolenaarb9ce83e2012-08-23 12:59:02 +02003164 /* longer ago */
Bram Moolenaar5e3d6ca2011-01-22 21:25:11 +01003165 (void)strftime((char *)buf, buflen, "%Y/%m/%d %H:%M:%S", curtime);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003166 }
3167 else
3168#endif
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003169 vim_snprintf((char *)buf, buflen, _("%ld seconds ago"),
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003170 (long)(time(NULL) - tt));
3171}
3172
3173/*
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003174 * ":undojoin": continue adding to the last entry list
3175 */
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003176 void
3177ex_undojoin(eap)
Bram Moolenaarfff2bee2010-05-15 13:56:02 +02003178 exarg_T *eap UNUSED;
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003179{
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003180 if (curbuf->b_u_newhead == NULL)
3181 return; /* nothing changed before */
Bram Moolenaar57657d82006-04-21 22:12:41 +00003182 if (curbuf->b_u_curhead != NULL)
3183 {
3184 EMSG(_("E790: undojoin is not allowed after undo"));
3185 return;
3186 }
3187 if (!curbuf->b_u_synced)
3188 return; /* already unsynced */
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +01003189 if (get_undolevel() < 0)
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003190 return; /* no entries, nothing to do */
3191 else
3192 {
3193 /* Go back to the last entry */
3194 curbuf->b_u_curhead = curbuf->b_u_newhead;
3195 curbuf->b_u_synced = FALSE; /* no entries, nothing to do */
3196 }
3197}
3198
3199/*
Bram Moolenaar59f931e2010-07-24 20:27:03 +02003200 * Called after writing or reloading the file and setting b_changed to FALSE.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003201 * Now an undo means that the buffer is modified.
3202 */
3203 void
3204u_unchanged(buf)
3205 buf_T *buf;
3206{
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003207 u_unch_branch(buf->b_u_oldhead);
3208 buf->b_did_warn = FALSE;
3209}
3210
Bram Moolenaar730cde92010-06-27 05:18:54 +02003211/*
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003212 * After reloading a buffer which was saved for 'undoreload': Find the first
3213 * line that was changed and set the cursor there.
3214 */
3215 void
3216u_find_first_changed()
3217{
3218 u_header_T *uhp = curbuf->b_u_newhead;
3219 u_entry_T *uep;
3220 linenr_T lnum;
3221
3222 if (curbuf->b_u_curhead != NULL || uhp == NULL)
3223 return; /* undid something in an autocmd? */
3224
3225 /* Check that the last undo block was for the whole file. */
3226 uep = uhp->uh_entry;
3227 if (uep->ue_top != 0 || uep->ue_bot != 0)
3228 return;
3229
3230 for (lnum = 1; lnum < curbuf->b_ml.ml_line_count
3231 && lnum <= uep->ue_size; ++lnum)
3232 if (STRCMP(ml_get_buf(curbuf, lnum, FALSE),
3233 uep->ue_array[lnum - 1]) != 0)
3234 {
3235 clearpos(&(uhp->uh_cursor));
3236 uhp->uh_cursor.lnum = lnum;
3237 return;
3238 }
3239 if (curbuf->b_ml.ml_line_count != uep->ue_size)
3240 {
3241 /* lines added or deleted at the end, put the cursor there */
3242 clearpos(&(uhp->uh_cursor));
3243 uhp->uh_cursor.lnum = lnum;
3244 }
3245}
3246
3247/*
Bram Moolenaar730cde92010-06-27 05:18:54 +02003248 * Increase the write count, store it in the last undo header, what would be
3249 * used for "u".
3250 */
3251 void
3252u_update_save_nr(buf)
3253 buf_T *buf;
3254{
3255 u_header_T *uhp;
3256
3257 ++buf->b_u_save_nr_last;
3258 buf->b_u_save_nr_cur = buf->b_u_save_nr_last;
3259 uhp = buf->b_u_curhead;
3260 if (uhp != NULL)
3261 uhp = uhp->uh_next.ptr;
3262 else
3263 uhp = buf->b_u_newhead;
3264 if (uhp != NULL)
3265 uhp->uh_save_nr = buf->b_u_save_nr_last;
3266}
3267
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003268 static void
3269u_unch_branch(uhp)
3270 u_header_T *uhp;
3271{
Bram Moolenaar1e607892006-03-13 22:15:53 +00003272 u_header_T *uh;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003273
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003274 for (uh = uhp; uh != NULL; uh = uh->uh_prev.ptr)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003275 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003276 uh->uh_flags |= UH_CHANGED;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003277 if (uh->uh_alt_next.ptr != NULL)
3278 u_unch_branch(uh->uh_alt_next.ptr); /* recursive */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003279 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003280}
3281
3282/*
3283 * Get pointer to last added entry.
3284 * If it's not valid, give an error message and return NULL.
3285 */
3286 static u_entry_T *
3287u_get_headentry()
3288{
3289 if (curbuf->b_u_newhead == NULL || curbuf->b_u_newhead->uh_entry == NULL)
3290 {
3291 EMSG(_("E439: undo list corrupt"));
3292 return NULL;
3293 }
3294 return curbuf->b_u_newhead->uh_entry;
3295}
3296
3297/*
3298 * u_getbot(): compute the line number of the previous u_save
3299 * It is called only when b_u_synced is FALSE.
3300 */
3301 static void
3302u_getbot()
3303{
3304 u_entry_T *uep;
3305 linenr_T extra;
3306
3307 uep = u_get_headentry(); /* check for corrupt undo list */
3308 if (uep == NULL)
3309 return;
3310
3311 uep = curbuf->b_u_newhead->uh_getbot_entry;
3312 if (uep != NULL)
3313 {
3314 /*
3315 * the new ue_bot is computed from the number of lines that has been
3316 * inserted (0 - deleted) since calling u_save. This is equal to the
3317 * old line count subtracted from the current line count.
3318 */
3319 extra = curbuf->b_ml.ml_line_count - uep->ue_lcount;
3320 uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra;
3321 if (uep->ue_bot < 1 || uep->ue_bot > curbuf->b_ml.ml_line_count)
3322 {
3323 EMSG(_("E440: undo line missing"));
3324 uep->ue_bot = uep->ue_top + 1; /* assume all lines deleted, will
3325 * get all the old lines back
3326 * without deleting the current
3327 * ones */
3328 }
3329
3330 curbuf->b_u_newhead->uh_getbot_entry = NULL;
3331 }
3332
3333 curbuf->b_u_synced = TRUE;
3334}
3335
3336/*
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003337 * Free one header "uhp" and its entry list and adjust the pointers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003338 */
3339 static void
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003340u_freeheader(buf, uhp, uhpp)
Bram Moolenaar26a60b42005-02-22 08:49:11 +00003341 buf_T *buf;
Bram Moolenaar1e607892006-03-13 22:15:53 +00003342 u_header_T *uhp;
3343 u_header_T **uhpp; /* if not NULL reset when freeing this header */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003344{
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003345 u_header_T *uhap;
3346
Bram Moolenaar1e607892006-03-13 22:15:53 +00003347 /* When there is an alternate redo list free that branch completely,
3348 * because we can never go there. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003349 if (uhp->uh_alt_next.ptr != NULL)
3350 u_freebranch(buf, uhp->uh_alt_next.ptr, uhpp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003351
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003352 if (uhp->uh_alt_prev.ptr != NULL)
3353 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003354
Bram Moolenaar1e607892006-03-13 22:15:53 +00003355 /* Update the links in the list to remove the header. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003356 if (uhp->uh_next.ptr == NULL)
3357 buf->b_u_oldhead = uhp->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003358 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003359 uhp->uh_next.ptr->uh_prev.ptr = uhp->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003360
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003361 if (uhp->uh_prev.ptr == NULL)
3362 buf->b_u_newhead = uhp->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003363 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003364 for (uhap = uhp->uh_prev.ptr; uhap != NULL;
3365 uhap = uhap->uh_alt_next.ptr)
3366 uhap->uh_next.ptr = uhp->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003367
Bram Moolenaar1e607892006-03-13 22:15:53 +00003368 u_freeentries(buf, uhp, uhpp);
3369}
3370
3371/*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003372 * Free an alternate branch and any following alternate branches.
Bram Moolenaar1e607892006-03-13 22:15:53 +00003373 */
3374 static void
3375u_freebranch(buf, uhp, uhpp)
3376 buf_T *buf;
3377 u_header_T *uhp;
3378 u_header_T **uhpp; /* if not NULL reset when freeing this header */
3379{
3380 u_header_T *tofree, *next;
3381
Bram Moolenaar07d06772007-11-10 21:51:15 +00003382 /* If this is the top branch we may need to use u_freeheader() to update
3383 * all the pointers. */
3384 if (uhp == buf->b_u_oldhead)
3385 {
Bram Moolenaaraa887322013-11-07 03:04:11 +01003386 while (buf->b_u_oldhead != NULL)
3387 u_freeheader(buf, buf->b_u_oldhead, uhpp);
Bram Moolenaar07d06772007-11-10 21:51:15 +00003388 return;
3389 }
3390
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003391 if (uhp->uh_alt_prev.ptr != NULL)
3392 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +00003393
3394 next = uhp;
3395 while (next != NULL)
3396 {
3397 tofree = next;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003398 if (tofree->uh_alt_next.ptr != NULL)
3399 u_freebranch(buf, tofree->uh_alt_next.ptr, uhpp); /* recursive */
3400 next = tofree->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00003401 u_freeentries(buf, tofree, uhpp);
3402 }
3403}
3404
3405/*
3406 * Free all the undo entries for one header and the header itself.
3407 * This means that "uhp" is invalid when returning.
3408 */
3409 static void
3410u_freeentries(buf, uhp, uhpp)
3411 buf_T *buf;
3412 u_header_T *uhp;
3413 u_header_T **uhpp; /* if not NULL reset when freeing this header */
3414{
3415 u_entry_T *uep, *nuep;
3416
3417 /* Check for pointers to the header that become invalid now. */
3418 if (buf->b_u_curhead == uhp)
3419 buf->b_u_curhead = NULL;
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003420 if (buf->b_u_newhead == uhp)
3421 buf->b_u_newhead = NULL; /* freeing the newest entry */
Bram Moolenaar1e607892006-03-13 22:15:53 +00003422 if (uhpp != NULL && uhp == *uhpp)
3423 *uhpp = NULL;
3424
3425 for (uep = uhp->uh_entry; uep != NULL; uep = nuep)
3426 {
3427 nuep = uep->ue_next;
3428 u_freeentry(uep, uep->ue_size);
3429 }
3430
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003431#ifdef U_DEBUG
3432 uhp->uh_magic = 0;
3433#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003434 vim_free((char_u *)uhp);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00003435 --buf->b_u_numhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003436}
3437
3438/*
3439 * free entry 'uep' and 'n' lines in uep->ue_array[]
3440 */
3441 static void
3442u_freeentry(uep, n)
3443 u_entry_T *uep;
3444 long n;
3445{
Bram Moolenaar8d343302005-07-12 22:46:17 +00003446 while (n > 0)
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003447 vim_free(uep->ue_array[--n]);
3448 vim_free((char_u *)uep->ue_array);
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003449#ifdef U_DEBUG
3450 uep->ue_magic = 0;
3451#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003452 vim_free((char_u *)uep);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003453}
3454
3455/*
3456 * invalidate the undo buffer; called when storage has already been released
3457 */
3458 void
3459u_clearall(buf)
3460 buf_T *buf;
3461{
3462 buf->b_u_newhead = buf->b_u_oldhead = buf->b_u_curhead = NULL;
3463 buf->b_u_synced = TRUE;
3464 buf->b_u_numhead = 0;
3465 buf->b_u_line_ptr = NULL;
3466 buf->b_u_line_lnum = 0;
3467}
3468
3469/*
3470 * save the line "lnum" for the "U" command
3471 */
3472 void
3473u_saveline(lnum)
3474 linenr_T lnum;
3475{
3476 if (lnum == curbuf->b_u_line_lnum) /* line is already saved */
3477 return;
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00003478 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) /* should never happen */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003479 return;
3480 u_clearline();
3481 curbuf->b_u_line_lnum = lnum;
3482 if (curwin->w_cursor.lnum == lnum)
3483 curbuf->b_u_line_colnr = curwin->w_cursor.col;
3484 else
3485 curbuf->b_u_line_colnr = 0;
3486 if ((curbuf->b_u_line_ptr = u_save_line(lnum)) == NULL)
3487 do_outofmem_msg((long_u)0);
3488}
3489
3490/*
3491 * clear the line saved for the "U" command
3492 * (this is used externally for crossing a line while in insert mode)
3493 */
3494 void
3495u_clearline()
3496{
3497 if (curbuf->b_u_line_ptr != NULL)
3498 {
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003499 vim_free(curbuf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003500 curbuf->b_u_line_ptr = NULL;
3501 curbuf->b_u_line_lnum = 0;
3502 }
3503}
3504
3505/*
3506 * Implementation of the "U" command.
3507 * Differentiation from vi: "U" can be undone with the next "U".
3508 * We also allow the cursor to be in another line.
Bram Moolenaard04b7502010-07-08 22:27:55 +02003509 * Careful: may trigger autocommands that reload the buffer.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003510 */
3511 void
3512u_undoline()
3513{
3514 colnr_T t;
3515 char_u *oldp;
3516
3517 if (undo_off)
3518 return;
3519
Bram Moolenaare3300c82008-02-13 14:21:38 +00003520 if (curbuf->b_u_line_ptr == NULL
3521 || curbuf->b_u_line_lnum > curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003522 {
3523 beep_flush();
3524 return;
3525 }
Bram Moolenaare3300c82008-02-13 14:21:38 +00003526
3527 /* first save the line for the 'u' command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003528 if (u_savecommon(curbuf->b_u_line_lnum - 1,
Bram Moolenaar59f931e2010-07-24 20:27:03 +02003529 curbuf->b_u_line_lnum + 1, (linenr_T)0, FALSE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003530 return;
3531 oldp = u_save_line(curbuf->b_u_line_lnum);
3532 if (oldp == NULL)
3533 {
3534 do_outofmem_msg((long_u)0);
3535 return;
3536 }
3537 ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, TRUE);
3538 changed_bytes(curbuf->b_u_line_lnum, 0);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003539 vim_free(curbuf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003540 curbuf->b_u_line_ptr = oldp;
3541
3542 t = curbuf->b_u_line_colnr;
3543 if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum)
3544 curbuf->b_u_line_colnr = curwin->w_cursor.col;
3545 curwin->w_cursor.col = t;
3546 curwin->w_cursor.lnum = curbuf->b_u_line_lnum;
Bram Moolenaare3300c82008-02-13 14:21:38 +00003547 check_cursor_col();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003548}
3549
3550/*
Bram Moolenaar26a60b42005-02-22 08:49:11 +00003551 * Free all allocated memory blocks for the buffer 'buf'.
3552 */
3553 void
3554u_blockfree(buf)
3555 buf_T *buf;
3556{
Bram Moolenaar1e607892006-03-13 22:15:53 +00003557 while (buf->b_u_oldhead != NULL)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003558 u_freeheader(buf, buf->b_u_oldhead, NULL);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003559 vim_free(buf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003560}
3561
3562/*
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003563 * u_save_line(): allocate memory and copy line 'lnum' into it.
3564 * Returns NULL when out of memory.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003565 */
3566 static char_u *
3567u_save_line(lnum)
3568 linenr_T lnum;
3569{
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003570 return vim_strsave(ml_get(lnum));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003571}
3572
3573/*
3574 * Check if the 'modified' flag is set, or 'ff' has changed (only need to
3575 * check the first character, because it can only be "dos", "unix" or "mac").
3576 * "nofile" and "scratch" type buffers are considered to always be unchanged.
3577 */
3578 int
3579bufIsChanged(buf)
3580 buf_T *buf;
3581{
3582 return
3583#ifdef FEAT_QUICKFIX
3584 !bt_dontwrite(buf) &&
3585#endif
Bram Moolenaar164c60f2011-01-22 00:11:50 +01003586 (buf->b_changed || file_ff_differs(buf, TRUE));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003587}
3588
3589 int
3590curbufIsChanged()
3591{
3592 return
3593#ifdef FEAT_QUICKFIX
3594 !bt_dontwrite(curbuf) &&
3595#endif
Bram Moolenaar164c60f2011-01-22 00:11:50 +01003596 (curbuf->b_changed || file_ff_differs(curbuf, TRUE));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003597}
Bram Moolenaara800b422010-06-27 01:15:55 +02003598
3599#if defined(FEAT_EVAL) || defined(PROTO)
3600/*
3601 * For undotree(): Append the list of undo blocks at "first_uhp" to "list".
3602 * Recursive.
3603 */
3604 void
3605u_eval_tree(first_uhp, list)
3606 u_header_T *first_uhp;
3607 list_T *list;
3608{
3609 u_header_T *uhp = first_uhp;
3610 dict_T *dict;
3611
3612 while (uhp != NULL)
3613 {
3614 dict = dict_alloc();
3615 if (dict == NULL)
3616 return;
3617 dict_add_nr_str(dict, "seq", uhp->uh_seq, NULL);
Bram Moolenaarb2c03502010-07-02 20:20:09 +02003618 dict_add_nr_str(dict, "time", (long)uhp->uh_time, NULL);
Bram Moolenaara800b422010-06-27 01:15:55 +02003619 if (uhp == curbuf->b_u_newhead)
3620 dict_add_nr_str(dict, "newhead", 1, NULL);
3621 if (uhp == curbuf->b_u_curhead)
3622 dict_add_nr_str(dict, "curhead", 1, NULL);
3623 if (uhp->uh_save_nr > 0)
3624 dict_add_nr_str(dict, "save", uhp->uh_save_nr, NULL);
3625
3626 if (uhp->uh_alt_next.ptr != NULL)
3627 {
3628 list_T *alt_list = list_alloc();
3629
3630 if (alt_list != NULL)
3631 {
3632 /* Recursive call to add alternate undo tree. */
3633 u_eval_tree(uhp->uh_alt_next.ptr, alt_list);
3634 dict_add_list(dict, "alt", alt_list);
3635 }
3636 }
3637
3638 list_append_dict(list, dict);
3639 uhp = uhp->uh_prev.ptr;
3640 }
3641}
3642#endif