blob: 1661c8074beaabfab732ffeb9d0e49a79787bb7a [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{
932 if (bi->bi_used > 0)
933 {
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)
995 buf[bufi++] = nr >> (i * 8);
996 return undo_write(bi, buf, (size_t)len);
997}
998
999/*
1000 * Write the pointer to an undo header. Instead of writing the pointer itself
1001 * we use the sequence number of the header. This is converted back to
1002 * pointers when reading. */
1003 static void
1004put_header_ptr(bi, uhp)
1005 bufinfo_T *bi;
1006 u_header_T *uhp;
1007{
1008 undo_write_bytes(bi, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
Bram Moolenaar191e0a22010-06-14 01:39:13 +02001009}
1010
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001011 static int
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001012undo_read_4c(bi)
1013 bufinfo_T *bi;
1014{
1015#ifdef FEAT_CRYPT
1016 if (bi->bi_buffer != NULL)
1017 {
1018 char_u buf[4];
1019 int n;
1020
1021 undo_read(bi, buf, (size_t)4);
1022 n = (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3];
1023 return n;
1024 }
1025#endif
1026 return get4c(bi->bi_fp);
1027}
1028
1029 static int
1030undo_read_2c(bi)
1031 bufinfo_T *bi;
1032{
1033#ifdef FEAT_CRYPT
1034 if (bi->bi_buffer != NULL)
1035 {
1036 char_u buf[2];
1037 int n;
1038
1039 undo_read(bi, buf, (size_t)2);
1040 n = (buf[0] << 8) + buf[1];
1041 return n;
1042 }
1043#endif
1044 return get2c(bi->bi_fp);
1045}
1046
1047 static int
1048undo_read_byte(bi)
1049 bufinfo_T *bi;
1050{
1051#ifdef FEAT_CRYPT
1052 if (bi->bi_buffer != NULL)
1053 {
1054 char_u buf[1];
1055
1056 undo_read(bi, buf, (size_t)1);
1057 return buf[0];
1058 }
1059#endif
1060 return getc(bi->bi_fp);
1061}
1062
1063 static time_t
1064undo_read_time(bi)
1065 bufinfo_T *bi;
1066{
1067#ifdef FEAT_CRYPT
1068 if (bi->bi_buffer != NULL)
1069 {
1070 char_u buf[8];
1071 time_t n = 0;
1072 int i;
1073
1074 undo_read(bi, buf, (size_t)8);
1075 for (i = 0; i < 8; ++i)
1076 n = (n << 8) + buf[i];
1077 return n;
1078 }
1079#endif
1080 return get8ctime(bi->bi_fp);
1081}
1082
1083/*
1084 * Read "buffer[size]" from the undo file.
1085 * Return OK or FAIL.
1086 */
1087 static int
1088undo_read(bi, buffer, size)
1089 bufinfo_T *bi;
1090 char_u *buffer;
1091 size_t size;
1092{
1093#ifdef FEAT_CRYPT
1094 if (bi->bi_buffer != NULL)
1095 {
1096 int size_todo = size;
1097 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;
1122 size_todo -= n;
1123 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 Moolenaara8ffcbb2010-06-21 06:15:46 +02001576#ifdef FEAT_CRYPT
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001577 bi.bi_state = NULL;
1578 bi.bi_buffer = NULL;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001579#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02001580
1581 if (name == NULL)
1582 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001583 file_name = u_get_undo_file_name(buf->b_ffname, FALSE);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001584 if (file_name == NULL)
1585 {
1586 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001587 {
1588 verbose_enter();
1589 smsg((char_u *)
1590 _("Cannot write undo file in any directory in 'undodir'"));
1591 verbose_leave();
1592 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001593 return;
1594 }
1595 }
1596 else
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001597 file_name = name;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001598
1599 /*
1600 * Decide about the permission to use for the undo file. If the buffer
1601 * has a name use the permission of the original file. Otherwise only
1602 * allow the user to access the undo file.
1603 */
1604 perm = 0600;
1605 if (buf->b_ffname != NULL)
1606 {
1607#ifdef UNIX
1608 if (mch_stat((char *)buf->b_ffname, &st_old) >= 0)
1609 {
1610 perm = st_old.st_mode;
1611 st_old_valid = TRUE;
1612 }
1613#else
1614 perm = mch_getperm(buf->b_ffname);
1615 if (perm < 0)
1616 perm = 0600;
1617#endif
1618 }
1619
1620 /* strip any s-bit */
1621 perm = perm & 0777;
1622
1623 /* If the undo file already exists, verify that it actually is an undo
1624 * file, and delete it. */
1625 if (mch_getperm(file_name) >= 0)
1626 {
1627 if (name == NULL || !forceit)
1628 {
1629 /* Check we can read it and it's an undo file. */
1630 fd = mch_open((char *)file_name, O_RDONLY|O_EXTRA, 0);
1631 if (fd < 0)
1632 {
1633 if (name != NULL || p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001634 {
1635 if (name == NULL)
1636 verbose_enter();
1637 smsg((char_u *)
1638 _("Will not overwrite with undo file, cannot read: %s"),
Bram Moolenaar9db58062010-05-29 20:33:07 +02001639 file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001640 if (name == NULL)
1641 verbose_leave();
1642 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001643 goto theend;
1644 }
1645 else
1646 {
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001647 char_u mbuf[UF_START_MAGIC_LEN];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001648 int len;
1649
Bram Moolenaar540fc6f2010-12-17 16:27:16 +01001650 len = read_eintr(fd, mbuf, UF_START_MAGIC_LEN);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001651 close(fd);
1652 if (len < UF_START_MAGIC_LEN
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001653 || memcmp(mbuf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001654 {
1655 if (name != NULL || p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001656 {
1657 if (name == NULL)
1658 verbose_enter();
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001659 smsg((char_u *)
1660 _("Will not overwrite, this is not an undo file: %s"),
Bram Moolenaar9db58062010-05-29 20:33:07 +02001661 file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001662 if (name == NULL)
1663 verbose_leave();
1664 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001665 goto theend;
1666 }
1667 }
1668 }
1669 mch_remove(file_name);
1670 }
1671
Bram Moolenaar504a8212010-05-30 17:17:42 +02001672 /* If there is no undo information at all, quit here after deleting any
1673 * existing undo file. */
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001674 if (buf->b_u_numhead == 0 && buf->b_u_line_ptr == NULL)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001675 {
1676 if (p_verbose > 0)
Bram Moolenaar97ea5112010-06-12 06:46:44 +02001677 verb_msg((char_u *)_("Skipping undo file write, nothing to undo"));
Bram Moolenaar504a8212010-05-30 17:17:42 +02001678 goto theend;
1679 }
1680
Bram Moolenaar9db58062010-05-29 20:33:07 +02001681 fd = mch_open((char *)file_name,
1682 O_CREAT|O_EXTRA|O_WRONLY|O_EXCL|O_NOFOLLOW, perm);
1683 if (fd < 0)
1684 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001685 EMSG2(_(e_not_open), file_name);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001686 goto theend;
1687 }
1688 (void)mch_setperm(file_name, perm);
1689 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001690 {
1691 verbose_enter();
Bram Moolenaar9db58062010-05-29 20:33:07 +02001692 smsg((char_u *)_("Writing undo file: %s"), file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001693 verbose_leave();
1694 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001695
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001696#ifdef U_DEBUG
Bram Moolenaar504a8212010-05-30 17:17:42 +02001697 /* Check there is no problem in undo info before writing. */
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001698 u_check(FALSE);
1699#endif
1700
Bram Moolenaar9db58062010-05-29 20:33:07 +02001701#ifdef UNIX
1702 /*
1703 * Try to set the group of the undo file same as the original file. If
1704 * this fails, set the protection bits for the group same as the
1705 * protection bits for others.
1706 */
Bram Moolenaarce69e822010-07-21 20:31:07 +02001707 if (st_old_valid
1708 && mch_stat((char *)file_name, &st_new) >= 0
1709 && st_new.st_gid != st_old.st_gid
Bram Moolenaar9db58062010-05-29 20:33:07 +02001710# ifdef HAVE_FCHOWN /* sequent-ptx lacks fchown() */
Bram Moolenaarce69e822010-07-21 20:31:07 +02001711 && fchown(fd, (uid_t)-1, st_old.st_gid) != 0
Bram Moolenaar9db58062010-05-29 20:33:07 +02001712# endif
1713 )
1714 mch_setperm(file_name, (perm & 0707) | ((perm & 07) << 3));
Bram Moolenaar5bd32f42014-04-02 14:05:38 +02001715# if defined(HAVE_SELINUX) || defined(HAVE_SMACK)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001716 if (buf->b_ffname != NULL)
1717 mch_copy_sec(buf->b_ffname, file_name);
1718# endif
1719#endif
1720
1721 fp = fdopen(fd, "w");
1722 if (fp == NULL)
1723 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001724 EMSG2(_(e_not_open), file_name);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001725 close(fd);
1726 mch_remove(file_name);
1727 goto theend;
1728 }
1729
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02001730 /* Undo must be synced. */
1731 u_sync(TRUE);
1732
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001733 /*
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001734 * Write the header. Initializes encryption, if enabled.
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001735 */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001736 bi.bi_buf = buf;
1737 bi.bi_fp = fp;
1738 if (serialize_header(&bi, hash) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001739 goto write_error;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001740
1741 /*
1742 * Iteratively serialize UHPs and their UEPs from the top down.
1743 */
1744 mark = ++lastmark;
1745 uhp = buf->b_u_oldhead;
1746 while (uhp != NULL)
1747 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001748 /* Serialize current UHP if we haven't seen it */
1749 if (uhp->uh_walk != mark)
1750 {
1751 uhp->uh_walk = mark;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001752#ifdef U_DEBUG
1753 ++headers_written;
1754#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001755 if (serialize_uhp(&bi, uhp) == FAIL)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001756 goto write_error;
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001757 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001758
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001759 /* Now walk through the tree - algorithm from undo_time(). */
1760 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != mark)
1761 uhp = uhp->uh_prev.ptr;
1762 else if (uhp->uh_alt_next.ptr != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001763 && uhp->uh_alt_next.ptr->uh_walk != mark)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001764 uhp = uhp->uh_alt_next.ptr;
1765 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02001766 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001767 uhp = uhp->uh_next.ptr;
1768 else if (uhp->uh_alt_prev.ptr != NULL)
1769 uhp = uhp->uh_alt_prev.ptr;
1770 else
1771 uhp = uhp->uh_next.ptr;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001772 }
1773
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001774 if (undo_write_bytes(&bi, (long_u)UF_HEADER_END_MAGIC, 2) == OK)
Bram Moolenaar9db58062010-05-29 20:33:07 +02001775 write_ok = TRUE;
1776#ifdef U_DEBUG
1777 if (headers_written != buf->b_u_numhead)
Bram Moolenaar570064c2013-06-10 20:25:10 +02001778 {
1779 EMSGN("Written %ld headers, ...", headers_written);
1780 EMSGN("... but numhead is %ld", buf->b_u_numhead);
1781 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02001782#endif
1783
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001784#ifdef FEAT_CRYPT
1785 if (bi.bi_state != NULL && undo_flush(&bi) == FAIL)
1786 write_ok = FALSE;
1787#endif
1788
Bram Moolenaar9db58062010-05-29 20:33:07 +02001789write_error:
1790 fclose(fp);
1791 if (!write_ok)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001792 EMSG2(_("E829: write error in undo file: %s"), file_name);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001793
1794#if defined(MACOS_CLASSIC) || defined(WIN3264)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001795 /* Copy file attributes; for systems where this can only be done after
1796 * closing the file. */
Bram Moolenaar9db58062010-05-29 20:33:07 +02001797 if (buf->b_ffname != NULL)
1798 (void)mch_copy_file_attribute(buf->b_ffname, file_name);
1799#endif
1800#ifdef HAVE_ACL
1801 if (buf->b_ffname != NULL)
1802 {
1803 vim_acl_T acl;
1804
1805 /* For systems that support ACL: get the ACL from the original file. */
1806 acl = mch_get_acl(buf->b_ffname);
1807 mch_set_acl(file_name, acl);
Bram Moolenaard2aed442012-06-01 13:46:12 +02001808 mch_free_acl(acl);
Bram Moolenaar9db58062010-05-29 20:33:07 +02001809 }
1810#endif
1811
1812theend:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001813#ifdef FEAT_CRYPT
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001814 if (bi.bi_state != NULL)
1815 crypt_free_state(bi.bi_state);
1816 vim_free(bi.bi_buffer);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001817#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02001818 if (file_name != name)
1819 vim_free(file_name);
1820}
1821
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001822/*
1823 * Load the undo tree from an undo file.
1824 * If "name" is not NULL use it as the undo file name. This also means being
1825 * a bit more verbose.
1826 * Otherwise use curbuf->b_ffname to generate the undo file name.
1827 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
1828 */
1829 void
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001830u_read_undo(name, hash, orig_name)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001831 char_u *name;
1832 char_u *hash;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001833 char_u *orig_name;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001834{
1835 char_u *file_name;
1836 FILE *fp;
Bram Moolenaar9db58062010-05-29 20:33:07 +02001837 long version, str_len;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001838 char_u *line_ptr = NULL;
1839 linenr_T line_lnum;
1840 colnr_T line_colnr;
1841 linenr_T line_count;
Bram Moolenaar442b4222010-05-24 21:34:22 +02001842 int num_head = 0;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001843 long old_header_seq, new_header_seq, cur_header_seq;
1844 long seq_last, seq_cur;
Bram Moolenaarb2c03502010-07-02 20:20:09 +02001845 long last_save_nr = 0;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001846 short old_idx = -1, new_idx = -1, cur_idx = -1;
1847 long num_read_uhps = 0;
1848 time_t seq_time;
1849 int i, j;
1850 int c;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001851 u_header_T *uhp;
1852 u_header_T **uhp_table = NULL;
1853 char_u read_hash[UNDO_HASH_SIZE];
Bram Moolenaar9db58062010-05-29 20:33:07 +02001854 char_u magic_buf[UF_START_MAGIC_LEN];
1855#ifdef U_DEBUG
1856 int *uhp_table_used;
1857#endif
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001858#ifdef UNIX
1859 struct stat st_orig;
1860 struct stat st_undo;
1861#endif
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001862 bufinfo_T bi;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001863
1864 if (name == NULL)
1865 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001866 file_name = u_get_undo_file_name(curbuf->b_ffname, TRUE);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001867 if (file_name == NULL)
1868 return;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001869
1870#ifdef UNIX
1871 /* For safety we only read an undo file if the owner is equal to the
Bram Moolenaar3b262392013-09-08 15:40:49 +02001872 * owner of the text file or equal to the current user. */
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001873 if (mch_stat((char *)orig_name, &st_orig) >= 0
1874 && mch_stat((char *)file_name, &st_undo) >= 0
Bram Moolenaar3b262392013-09-08 15:40:49 +02001875 && st_orig.st_uid != st_undo.st_uid
1876 && st_undo.st_uid != getuid())
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001877 {
1878 if (p_verbose > 0)
1879 {
1880 verbose_enter();
1881 smsg((char_u *)_("Not reading undo file, owner differs: %s"),
1882 file_name);
1883 verbose_leave();
1884 }
1885 return;
1886 }
1887#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001888 }
1889 else
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001890 file_name = name;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001891
1892 if (p_verbose > 0)
Bram Moolenaar504a8212010-05-30 17:17:42 +02001893 {
1894 verbose_enter();
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001895 smsg((char_u *)_("Reading undo file: %s"), file_name);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001896 verbose_leave();
1897 }
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001898
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001899 fp = mch_fopen((char *)file_name, "r");
1900 if (fp == NULL)
1901 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001902 if (name != NULL || p_verbose > 0)
1903 EMSG2(_("E822: Cannot open undo file for reading: %s"), file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001904 goto error;
1905 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001906 bi.bi_buf = curbuf;
1907 bi.bi_fp = fp;
1908#ifdef FEAT_CRYPT
1909 bi.bi_state = NULL;
1910 bi.bi_buffer = NULL;
1911#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001912
Bram Moolenaar9db58062010-05-29 20:33:07 +02001913 /*
1914 * Read the undo file header.
1915 */
1916 if (fread(magic_buf, UF_START_MAGIC_LEN, 1, fp) != 1
1917 || memcmp(magic_buf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001918 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001919 EMSG2(_("E823: Not an undo file: %s"), file_name);
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001920 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001921 }
1922 version = get2c(fp);
Bram Moolenaar69154f22010-07-18 21:42:34 +02001923 if (version == UF_VERSION_CRYPT)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001924 {
1925#ifdef FEAT_CRYPT
Bram Moolenaar56be9502010-06-06 14:20:26 +02001926 if (*curbuf->b_p_key == NUL)
1927 {
1928 EMSG2(_("E832: Non-encrypted file has encrypted undo file: %s"),
1929 file_name);
1930 goto error;
1931 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001932 bi.bi_state = crypt_create_from_file(fp, curbuf->b_p_key);
1933 if (bi.bi_state == NULL)
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001934 {
1935 EMSG2(_("E826: Undo file decryption failed: %s"), file_name);
1936 goto error;
1937 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001938 if (crypt_whole_undofile(bi.bi_state->method_nr))
1939 {
1940 bi.bi_buffer = alloc(CRYPT_BUF_SIZE);
1941 if (bi.bi_buffer == NULL)
1942 {
1943 crypt_free_state(bi.bi_state);
1944 bi.bi_state = NULL;
1945 goto error;
1946 }
1947 bi.bi_avail = 0;
1948 bi.bi_used = 0;
1949 }
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001950#else
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001951 EMSG2(_("E827: Undo file is encrypted: %s"), file_name);
1952 goto error;
Bram Moolenaara3ff49f2010-05-30 22:48:02 +02001953#endif
1954 }
Bram Moolenaar69154f22010-07-18 21:42:34 +02001955 else if (version != UF_VERSION)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001956 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001957 EMSG2(_("E824: Incompatible undo file: %s"), file_name);
1958 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001959 }
1960
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001961 if (undo_read(&bi, read_hash, (size_t)UNDO_HASH_SIZE) == FAIL)
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001962 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02001963 corruption_error("hash", file_name);
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001964 goto error;
Bram Moolenaarcdf04202010-05-29 15:11:47 +02001965 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001966 line_count = (linenr_T)undo_read_4c(&bi);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001967 if (memcmp(hash, read_hash, UNDO_HASH_SIZE) != 0
1968 || line_count != curbuf->b_ml.ml_line_count)
1969 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001970 if (p_verbose > 0 || name != NULL)
1971 {
Bram Moolenaar504a8212010-05-30 17:17:42 +02001972 if (name == NULL)
1973 verbose_enter();
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001974 give_warning((char_u *)
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001975 _("File contents changed, cannot use undo info"), TRUE);
Bram Moolenaar504a8212010-05-30 17:17:42 +02001976 if (name == NULL)
1977 verbose_leave();
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001978 }
1979 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001980 }
1981
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001982 /* Read undo data for "U" command. */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001983 str_len = undo_read_4c(&bi);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001984 if (str_len < 0)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001985 goto error;
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001986 if (str_len > 0)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001987 line_ptr = read_string_decrypt(&bi, str_len);
1988 line_lnum = (linenr_T)undo_read_4c(&bi);
1989 line_colnr = (colnr_T)undo_read_4c(&bi);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02001990 if (line_lnum < 0 || line_colnr < 0)
1991 {
1992 corruption_error("line lnum/col", file_name);
1993 goto error;
1994 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02001995
1996 /* Begin general undo data */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001997 old_header_seq = undo_read_4c(&bi);
1998 new_header_seq = undo_read_4c(&bi);
1999 cur_header_seq = undo_read_4c(&bi);
2000 num_head = undo_read_4c(&bi);
2001 seq_last = undo_read_4c(&bi);
2002 seq_cur = undo_read_4c(&bi);
2003 seq_time = undo_read_time(&bi);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002004
Bram Moolenaar69154f22010-07-18 21:42:34 +02002005 /* Optional header fields. */
2006 for (;;)
2007 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002008 int len = undo_read_byte(&bi);
Bram Moolenaar69154f22010-07-18 21:42:34 +02002009 int what;
Bram Moolenaara800b422010-06-27 01:15:55 +02002010
Bram Moolenaar69154f22010-07-18 21:42:34 +02002011 if (len == 0 || len == EOF)
2012 break;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002013 what = undo_read_byte(&bi);
Bram Moolenaar69154f22010-07-18 21:42:34 +02002014 switch (what)
2015 {
2016 case UF_LAST_SAVE_NR:
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002017 last_save_nr = undo_read_4c(&bi);
Bram Moolenaara800b422010-06-27 01:15:55 +02002018 break;
Bram Moolenaar69154f22010-07-18 21:42:34 +02002019 default:
2020 /* field not supported, skip */
2021 while (--len >= 0)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002022 (void)undo_read_byte(&bi);
Bram Moolenaara800b422010-06-27 01:15:55 +02002023 }
Bram Moolenaar69154f22010-07-18 21:42:34 +02002024 }
Bram Moolenaara800b422010-06-27 01:15:55 +02002025
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002026 /* uhp_table will store the freshly created undo headers we allocate
2027 * until we insert them into curbuf. The table remains sorted by the
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002028 * sequence numbers of the headers.
2029 * When there are no headers uhp_table is NULL. */
2030 if (num_head > 0)
2031 {
2032 uhp_table = (u_header_T **)U_ALLOC_LINE(
2033 num_head * sizeof(u_header_T *));
2034 if (uhp_table == NULL)
2035 goto error;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002036 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002037
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002038 while ((c = undo_read_2c(&bi)) == UF_HEADER_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002039 {
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02002040 if (num_read_uhps >= num_head)
2041 {
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02002042 corruption_error("num_head too small", file_name);
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02002043 goto error;
2044 }
2045
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002046 uhp = unserialize_uhp(&bi, file_name);
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02002047 if (uhp == NULL)
2048 goto error;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02002049 uhp_table[num_read_uhps++] = uhp;
2050 }
2051
2052 if (num_read_uhps != num_head)
2053 {
2054 corruption_error("num_head", file_name);
2055 goto error;
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002056 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02002057 if (c != UF_HEADER_END_MAGIC)
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002058 {
Bram Moolenaar9db58062010-05-29 20:33:07 +02002059 corruption_error("end marker", file_name);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002060 goto error;
2061 }
2062
Bram Moolenaar9db58062010-05-29 20:33:07 +02002063#ifdef U_DEBUG
2064 uhp_table_used = (int *)alloc_clear(
2065 (unsigned)(sizeof(int) * num_head + 1));
2066# define SET_FLAG(j) ++uhp_table_used[j]
2067#else
2068# define SET_FLAG(j)
2069#endif
2070
Bram Moolenaar6ed8ed82010-05-30 20:40:11 +02002071 /* We have put all of the headers into a table. Now we iterate through the
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002072 * table and swizzle each sequence number we have stored in uh_*_seq into
2073 * a pointer corresponding to the header with that sequence number. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002074 for (i = 0; i < num_head; i++)
2075 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002076 uhp = uhp_table[i];
2077 if (uhp == NULL)
2078 continue;
2079 for (j = 0; j < num_head; j++)
2080 if (uhp_table[j] != NULL && i != j
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002081 && uhp_table[i]->uh_seq == uhp_table[j]->uh_seq)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002082 {
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02002083 corruption_error("duplicate uh_seq", file_name);
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002084 goto error;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02002085 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002086 for (j = 0; j < num_head; j++)
2087 if (uhp_table[j] != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002088 && uhp_table[j]->uh_seq == uhp->uh_next.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002089 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002090 uhp->uh_next.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02002091 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002092 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002093 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002094 for (j = 0; j < num_head; j++)
2095 if (uhp_table[j] != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002096 && uhp_table[j]->uh_seq == uhp->uh_prev.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002097 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002098 uhp->uh_prev.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02002099 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002100 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002101 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002102 for (j = 0; j < num_head; j++)
2103 if (uhp_table[j] != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002104 && uhp_table[j]->uh_seq == uhp->uh_alt_next.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002105 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002106 uhp->uh_alt_next.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02002107 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002108 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002109 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002110 for (j = 0; j < num_head; j++)
2111 if (uhp_table[j] != NULL
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002112 && uhp_table[j]->uh_seq == uhp->uh_alt_prev.seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002113 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002114 uhp->uh_alt_prev.ptr = uhp_table[j];
Bram Moolenaar9db58062010-05-29 20:33:07 +02002115 SET_FLAG(j);
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002116 break;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002117 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002118 if (old_header_seq > 0 && old_idx < 0 && uhp->uh_seq == old_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002119 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002120 old_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002121 SET_FLAG(i);
2122 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002123 if (new_header_seq > 0 && new_idx < 0 && uhp->uh_seq == new_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002124 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002125 new_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002126 SET_FLAG(i);
2127 }
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002128 if (cur_header_seq > 0 && cur_idx < 0 && uhp->uh_seq == cur_header_seq)
Bram Moolenaar9db58062010-05-29 20:33:07 +02002129 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002130 cur_idx = i;
Bram Moolenaar9db58062010-05-29 20:33:07 +02002131 SET_FLAG(i);
2132 }
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002133 }
Bram Moolenaar9db58062010-05-29 20:33:07 +02002134
2135 /* Now that we have read the undo info successfully, free the current undo
2136 * info and use the info from the file. */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002137 u_blockfree(curbuf);
Bram Moolenaar9db58062010-05-29 20:33:07 +02002138 curbuf->b_u_oldhead = old_idx < 0 ? NULL : uhp_table[old_idx];
2139 curbuf->b_u_newhead = new_idx < 0 ? NULL : uhp_table[new_idx];
2140 curbuf->b_u_curhead = cur_idx < 0 ? NULL : uhp_table[cur_idx];
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002141 curbuf->b_u_line_ptr = line_ptr;
2142 curbuf->b_u_line_lnum = line_lnum;
2143 curbuf->b_u_line_colnr = line_colnr;
2144 curbuf->b_u_numhead = num_head;
2145 curbuf->b_u_seq_last = seq_last;
2146 curbuf->b_u_seq_cur = seq_cur;
Bram Moolenaara800b422010-06-27 01:15:55 +02002147 curbuf->b_u_time_cur = seq_time;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002148 curbuf->b_u_save_nr_last = last_save_nr;
Bram Moolenaardba01a02010-11-03 19:32:42 +01002149 curbuf->b_u_save_nr_cur = last_save_nr;
Bram Moolenaar6773b2b2010-05-30 16:01:37 +02002150
2151 curbuf->b_u_synced = TRUE;
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002152 vim_free(uhp_table);
Bram Moolenaar9db58062010-05-29 20:33:07 +02002153
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002154#ifdef U_DEBUG
Bram Moolenaar9db58062010-05-29 20:33:07 +02002155 for (i = 0; i < num_head; ++i)
2156 if (uhp_table_used[i] == 0)
2157 EMSGN("uhp_table entry %ld not used, leaking memory", i);
2158 vim_free(uhp_table_used);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002159 u_check(TRUE);
2160#endif
Bram Moolenaar9db58062010-05-29 20:33:07 +02002161
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002162 if (name != NULL)
2163 smsg((char_u *)_("Finished reading undo file %s"), file_name);
2164 goto theend;
2165
2166error:
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002167 vim_free(line_ptr);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002168 if (uhp_table != NULL)
2169 {
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002170 for (i = 0; i < num_read_uhps; i++)
2171 if (uhp_table[i] != NULL)
Bram Moolenaar6a18eb62010-05-26 21:21:00 +02002172 u_free_uhp(uhp_table[i]);
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002173 vim_free(uhp_table);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002174 }
2175
2176theend:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002177#ifdef FEAT_CRYPT
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02002178 if (bi.bi_state != NULL)
2179 crypt_free_state(bi.bi_state);
2180 vim_free(bi.bi_buffer);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002181#endif
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002182 if (fp != NULL)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02002183 fclose(fp);
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002184 if (file_name != name)
2185 vim_free(file_name);
2186 return;
2187}
2188
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002189#endif /* FEAT_PERSISTENT_UNDO */
2190
2191
Bram Moolenaar071d4272004-06-13 20:20:40 +00002192/*
2193 * If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible).
2194 * If 'cpoptions' does not contain 'u': Always undo.
2195 */
2196 void
2197u_undo(count)
2198 int count;
2199{
2200 /*
2201 * If we get an undo command while executing a macro, we behave like the
2202 * original vi. If this happens twice in one macro the result will not
2203 * be compatible.
2204 */
2205 if (curbuf->b_u_synced == FALSE)
2206 {
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002207 u_sync(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002208 count = 1;
2209 }
2210
2211 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
2212 undo_undoes = TRUE;
2213 else
2214 undo_undoes = !undo_undoes;
2215 u_doit(count);
2216}
2217
2218/*
2219 * If 'cpoptions' contains 'u': Repeat the previous undo or redo.
2220 * If 'cpoptions' does not contain 'u': Always redo.
2221 */
2222 void
2223u_redo(count)
2224 int count;
2225{
2226 if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
2227 undo_undoes = FALSE;
2228 u_doit(count);
2229}
2230
2231/*
2232 * Undo or redo, depending on 'undo_undoes', 'count' times.
2233 */
2234 static void
Bram Moolenaarca003e12006-03-17 23:19:38 +00002235u_doit(startcount)
2236 int startcount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002237{
Bram Moolenaarca003e12006-03-17 23:19:38 +00002238 int count = startcount;
2239
Bram Moolenaar8ada17c2006-01-19 22:16:24 +00002240 if (!undo_allowed())
Bram Moolenaar071d4272004-06-13 20:20:40 +00002241 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002242
2243 u_newcount = 0;
2244 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00002245 if (curbuf->b_ml.ml_flags & ML_EMPTY)
2246 u_oldcount = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002247 while (count--)
2248 {
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002249 /* Do the change warning now, so that it triggers FileChangedRO when
2250 * needed. This may cause the file to be reloaded, that must happen
2251 * before we do anything, because it may change curbuf->b_u_curhead
2252 * and more. */
2253 change_warning(0);
2254
Bram Moolenaar071d4272004-06-13 20:20:40 +00002255 if (undo_undoes)
2256 {
2257 if (curbuf->b_u_curhead == NULL) /* first undo */
2258 curbuf->b_u_curhead = curbuf->b_u_newhead;
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +01002259 else if (get_undolevel() > 0) /* multi level undo */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002260 /* get next undo */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002261 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002262 /* nothing to undo */
2263 if (curbuf->b_u_numhead == 0 || curbuf->b_u_curhead == NULL)
2264 {
2265 /* stick curbuf->b_u_curhead at end */
2266 curbuf->b_u_curhead = curbuf->b_u_oldhead;
2267 beep_flush();
Bram Moolenaarca003e12006-03-17 23:19:38 +00002268 if (count == startcount - 1)
2269 {
2270 MSG(_("Already at oldest change"));
2271 return;
2272 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002273 break;
2274 }
2275
Bram Moolenaarca003e12006-03-17 23:19:38 +00002276 u_undoredo(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002277 }
2278 else
2279 {
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +01002280 if (curbuf->b_u_curhead == NULL || get_undolevel() <= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002281 {
2282 beep_flush(); /* nothing to redo */
Bram Moolenaarca003e12006-03-17 23:19:38 +00002283 if (count == startcount - 1)
2284 {
2285 MSG(_("Already at newest change"));
2286 return;
2287 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002288 break;
2289 }
2290
Bram Moolenaarca003e12006-03-17 23:19:38 +00002291 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002292
2293 /* Advance for next redo. Set "newhead" when at the end of the
2294 * redoable changes. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002295 if (curbuf->b_u_curhead->uh_prev.ptr == NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002296 curbuf->b_u_newhead = curbuf->b_u_curhead;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002297 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002298 }
2299 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00002300 u_undo_end(undo_undoes, FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002301}
2302
Bram Moolenaar1e607892006-03-13 22:15:53 +00002303/*
2304 * Undo or redo over the timeline.
2305 * When "step" is negative go back in time, otherwise goes forward in time.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002306 * When "sec" is FALSE make "step" steps, when "sec" is TRUE use "step" as
2307 * seconds.
Bram Moolenaar730cde92010-06-27 05:18:54 +02002308 * When "file" is TRUE use "step" as a number of file writes.
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002309 * When "absolute" is TRUE use "step" as the sequence number to jump to.
2310 * "sec" must be FALSE then.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002311 */
2312 void
Bram Moolenaar730cde92010-06-27 05:18:54 +02002313undo_time(step, sec, file, absolute)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002314 long step;
2315 int sec;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002316 int file;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002317 int absolute;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002318{
2319 long target;
2320 long closest;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002321 long closest_start;
2322 long closest_seq = 0;
2323 long val;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002324 u_header_T *uhp;
2325 u_header_T *last;
2326 int mark;
2327 int nomark;
2328 int round;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002329 int dosec = sec;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002330 int dofile = file;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002331 int above = FALSE;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002332 int did_undo = TRUE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002333
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00002334 /* First make sure the current undoable change is synced. */
2335 if (curbuf->b_u_synced == FALSE)
Bram Moolenaar779b74b2006-04-10 14:55:34 +00002336 u_sync(TRUE);
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00002337
Bram Moolenaar1e607892006-03-13 22:15:53 +00002338 u_newcount = 0;
2339 u_oldcount = 0;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00002340 if (curbuf->b_ml.ml_flags & ML_EMPTY)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002341 u_oldcount = -1;
2342
Bram Moolenaarca003e12006-03-17 23:19:38 +00002343 /* "target" is the node below which we want to be.
2344 * Init "closest" to a value we can't reach. */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002345 if (absolute)
2346 {
2347 target = step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002348 closest = -1;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002349 }
Bram Moolenaarca003e12006-03-17 23:19:38 +00002350 else
Bram Moolenaar1e607892006-03-13 22:15:53 +00002351 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002352 /* When doing computations with time_t subtract starttime, because
2353 * time_t converted to a long may result in a wrong number. */
Bram Moolenaar730cde92010-06-27 05:18:54 +02002354 if (dosec)
Bram Moolenaara800b422010-06-27 01:15:55 +02002355 target = (long)(curbuf->b_u_time_cur - starttime) + step;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002356 else if (dofile)
2357 {
2358 if (step < 0)
2359 {
2360 /* Going back to a previous write. If there were changes after
2361 * the last write, count that as moving one file-write, so
2362 * that ":earlier 1f" undoes all changes since the last save. */
2363 uhp = curbuf->b_u_curhead;
2364 if (uhp != NULL)
2365 uhp = uhp->uh_next.ptr;
2366 else
2367 uhp = curbuf->b_u_newhead;
2368 if (uhp != NULL && uhp->uh_save_nr != 0)
2369 /* "uh_save_nr" was set in the last block, that means
2370 * there were no changes since the last write */
2371 target = curbuf->b_u_save_nr_cur + step;
2372 else
2373 /* count the changes since the last write as one step */
2374 target = curbuf->b_u_save_nr_cur + step + 1;
2375 if (target <= 0)
2376 /* Go to before first write: before the oldest change. Use
2377 * the sequence number for that. */
2378 dofile = FALSE;
2379 }
2380 else
2381 {
2382 /* Moving forward to a newer write. */
2383 target = curbuf->b_u_save_nr_cur + step;
2384 if (target > curbuf->b_u_save_nr_last)
2385 {
2386 /* Go to after last write: after the latest change. Use
2387 * the sequence number for that. */
2388 target = curbuf->b_u_seq_last + 1;
2389 dofile = FALSE;
2390 }
2391 }
2392 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002393 else
2394 target = curbuf->b_u_seq_cur + step;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002395 if (step < 0)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002396 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002397 if (target < 0)
2398 target = 0;
2399 closest = -1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002400 }
2401 else
2402 {
Bram Moolenaar730cde92010-06-27 05:18:54 +02002403 if (dosec)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002404 closest = (long)(time(NULL) - starttime + 1);
Bram Moolenaar730cde92010-06-27 05:18:54 +02002405 else if (dofile)
2406 closest = curbuf->b_u_save_nr_last + 2;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002407 else
2408 closest = curbuf->b_u_seq_last + 2;
2409 if (target >= closest)
2410 target = closest - 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002411 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002412 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002413 closest_start = closest;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002414 closest_seq = curbuf->b_u_seq_cur;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002415
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002416 /*
2417 * May do this twice:
Bram Moolenaar1e607892006-03-13 22:15:53 +00002418 * 1. Search for "target", update "closest" to the best match found.
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002419 * 2. If "target" not found search for "closest".
2420 *
2421 * When using the closest time we use the sequence number in the second
2422 * round, because there may be several entries with the same time.
2423 */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002424 for (round = 1; round <= 2; ++round)
2425 {
2426 /* Find the path from the current state to where we want to go. The
2427 * desired state can be anywhere in the undo tree, need to go all over
2428 * it. We put "nomark" in uh_walk where we have been without success,
2429 * "mark" where it could possibly be. */
2430 mark = ++lastmark;
2431 nomark = ++lastmark;
2432
2433 if (curbuf->b_u_curhead == NULL) /* at leaf of the tree */
2434 uhp = curbuf->b_u_newhead;
2435 else
2436 uhp = curbuf->b_u_curhead;
2437
2438 while (uhp != NULL)
2439 {
2440 uhp->uh_walk = mark;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002441 if (dosec)
2442 val = (long)(uhp->uh_time - starttime);
2443 else if (dofile)
2444 val = uhp->uh_save_nr;
2445 else
2446 val = uhp->uh_seq;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002447
Bram Moolenaar730cde92010-06-27 05:18:54 +02002448 if (round == 1 && !(dofile && val == 0))
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002449 {
2450 /* Remember the header that is closest to the target.
2451 * It must be at least in the right direction (checked with
Bram Moolenaarca003e12006-03-17 23:19:38 +00002452 * "b_u_seq_cur"). When the timestamp is equal find the
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002453 * highest/lowest sequence number. */
Bram Moolenaarca003e12006-03-17 23:19:38 +00002454 if ((step < 0 ? uhp->uh_seq <= curbuf->b_u_seq_cur
2455 : uhp->uh_seq > curbuf->b_u_seq_cur)
2456 && ((dosec && val == closest)
2457 ? (step < 0
2458 ? uhp->uh_seq < closest_seq
2459 : uhp->uh_seq > closest_seq)
2460 : closest == closest_start
2461 || (val > target
2462 ? (closest > target
2463 ? val - target <= closest - target
2464 : val - target <= target - closest)
2465 : (closest > target
2466 ? target - val <= closest - target
2467 : target - val <= target - closest))))
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002468 {
2469 closest = val;
2470 closest_seq = uhp->uh_seq;
2471 }
2472 }
2473
2474 /* Quit searching when we found a match. But when searching for a
2475 * time we need to continue looking for the best uh_seq. */
2476 if (target == val && !dosec)
Bram Moolenaar730cde92010-06-27 05:18:54 +02002477 {
2478 target = uhp->uh_seq;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002479 break;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002480 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002481
2482 /* go down in the tree if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002483 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
2484 && uhp->uh_prev.ptr->uh_walk != mark)
2485 uhp = uhp->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002486
2487 /* go to alternate branch if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002488 else if (uhp->uh_alt_next.ptr != NULL
2489 && uhp->uh_alt_next.ptr->uh_walk != nomark
2490 && uhp->uh_alt_next.ptr->uh_walk != mark)
2491 uhp = uhp->uh_alt_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002492
2493 /* go up in the tree if we haven't been there and we are at the
2494 * start of alternate branches */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002495 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
2496 && uhp->uh_next.ptr->uh_walk != nomark
2497 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002498 {
2499 /* If still at the start we don't go through this change. */
2500 if (uhp == curbuf->b_u_curhead)
2501 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002502 uhp = uhp->uh_next.ptr;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002503 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002504
2505 else
2506 {
2507 /* need to backtrack; mark this node as useless */
2508 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002509 if (uhp->uh_alt_prev.ptr != NULL)
2510 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002511 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002512 uhp = uhp->uh_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002513 }
2514 }
2515
2516 if (uhp != NULL) /* found it */
2517 break;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002518
2519 if (absolute)
2520 {
Bram Moolenaar55debbe2010-05-23 23:34:36 +02002521 EMSGN(_("E830: Undo number %ld not found"), step);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002522 return;
2523 }
2524
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002525 if (closest == closest_start)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002526 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002527 if (step < 0)
2528 MSG(_("Already at oldest change"));
2529 else
2530 MSG(_("Already at newest change"));
Bram Moolenaar1e607892006-03-13 22:15:53 +00002531 return;
2532 }
2533
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002534 target = closest_seq;
2535 dosec = FALSE;
Bram Moolenaar730cde92010-06-27 05:18:54 +02002536 dofile = FALSE;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002537 if (step < 0)
2538 above = TRUE; /* stop above the header */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002539 }
2540
2541 /* If we found it: Follow the path to go to where we want to be. */
2542 if (uhp != NULL)
2543 {
2544 /*
2545 * First go up the tree as much as needed.
2546 */
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002547 while (!got_int)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002548 {
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002549 /* Do the change warning now, for the same reason as above. */
2550 change_warning(0);
2551
Bram Moolenaar1e607892006-03-13 22:15:53 +00002552 uhp = curbuf->b_u_curhead;
2553 if (uhp == NULL)
2554 uhp = curbuf->b_u_newhead;
2555 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002556 uhp = uhp->uh_next.ptr;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002557 if (uhp == NULL || uhp->uh_walk != mark
2558 || (uhp->uh_seq == target && !above))
Bram Moolenaar1e607892006-03-13 22:15:53 +00002559 break;
2560 curbuf->b_u_curhead = uhp;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002561 u_undoredo(TRUE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002562 uhp->uh_walk = nomark; /* don't go back down here */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002563 }
2564
2565 /*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002566 * And now go down the tree (redo), branching off where needed.
Bram Moolenaar1e607892006-03-13 22:15:53 +00002567 */
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002568 while (!got_int)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002569 {
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002570 /* Do the change warning now, for the same reason as above. */
2571 change_warning(0);
2572
2573 uhp = curbuf->b_u_curhead;
2574 if (uhp == NULL)
2575 break;
2576
Bram Moolenaar89ed3df2007-01-09 19:23:12 +00002577 /* Go back to the first branch with a mark. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002578 while (uhp->uh_alt_prev.ptr != NULL
2579 && uhp->uh_alt_prev.ptr->uh_walk == mark)
2580 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaar89ed3df2007-01-09 19:23:12 +00002581
Bram Moolenaar1e607892006-03-13 22:15:53 +00002582 /* Find the last branch with a mark, that's the one. */
2583 last = uhp;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002584 while (last->uh_alt_next.ptr != NULL
2585 && last->uh_alt_next.ptr->uh_walk == mark)
2586 last = last->uh_alt_next.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002587 if (last != uhp)
2588 {
2589 /* Make the used branch the first entry in the list of
2590 * alternatives to make "u" and CTRL-R take this branch. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002591 while (uhp->uh_alt_prev.ptr != NULL)
2592 uhp = uhp->uh_alt_prev.ptr;
2593 if (last->uh_alt_next.ptr != NULL)
2594 last->uh_alt_next.ptr->uh_alt_prev.ptr =
2595 last->uh_alt_prev.ptr;
2596 last->uh_alt_prev.ptr->uh_alt_next.ptr = last->uh_alt_next.ptr;
2597 last->uh_alt_prev.ptr = NULL;
2598 last->uh_alt_next.ptr = uhp;
2599 uhp->uh_alt_prev.ptr = last;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002600
Bram Moolenaar8f1f6292010-05-30 16:55:22 +02002601 if (curbuf->b_u_oldhead == uhp)
2602 curbuf->b_u_oldhead = last;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002603 uhp = last;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002604 if (uhp->uh_next.ptr != NULL)
2605 uhp->uh_next.ptr->uh_prev.ptr = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002606 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002607 curbuf->b_u_curhead = uhp;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002608
2609 if (uhp->uh_walk != mark)
2610 break; /* must have reached the target */
2611
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002612 /* Stop when going backwards in time and didn't find the exact
2613 * header we were looking for. */
2614 if (uhp->uh_seq == target && above)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002615 {
2616 curbuf->b_u_seq_cur = target - 1;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002617 break;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002618 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002619
Bram Moolenaarca003e12006-03-17 23:19:38 +00002620 u_undoredo(FALSE);
Bram Moolenaar1e607892006-03-13 22:15:53 +00002621
2622 /* Advance "curhead" to below the header we last used. If it
2623 * becomes NULL then we need to set "newhead" to this leaf. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002624 if (uhp->uh_prev.ptr == NULL)
Bram Moolenaar1e607892006-03-13 22:15:53 +00002625 curbuf->b_u_newhead = uhp;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002626 curbuf->b_u_curhead = uhp->uh_prev.ptr;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002627 did_undo = FALSE;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002628
2629 if (uhp->uh_seq == target) /* found it! */
2630 break;
2631
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002632 uhp = uhp->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002633 if (uhp == NULL || uhp->uh_walk != mark)
2634 {
Bram Moolenaarca003e12006-03-17 23:19:38 +00002635 /* Need to redo more but can't find it... */
Bram Moolenaar1e607892006-03-13 22:15:53 +00002636 EMSG2(_(e_intern2), "undo_time()");
2637 break;
2638 }
2639 }
2640 }
Bram Moolenaardb552d602006-03-23 22:59:57 +00002641 u_undo_end(did_undo, absolute);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002642}
2643
2644/*
2645 * u_undoredo: common code for undo and redo
2646 *
2647 * The lines in the file are replaced by the lines in the entry list at
2648 * curbuf->b_u_curhead. The replaced lines in the file are saved in the entry
2649 * list for the next undo/redo.
Bram Moolenaarca003e12006-03-17 23:19:38 +00002650 *
2651 * When "undo" is TRUE we go up in the tree, when FALSE we go down.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002652 */
2653 static void
Bram Moolenaarca003e12006-03-17 23:19:38 +00002654u_undoredo(undo)
2655 int undo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002656{
2657 char_u **newarray = NULL;
2658 linenr_T oldsize;
2659 linenr_T newsize;
2660 linenr_T top, bot;
2661 linenr_T lnum;
2662 linenr_T newlnum = MAXLNUM;
2663 long i;
2664 u_entry_T *uep, *nuep;
2665 u_entry_T *newlist = NULL;
2666 int old_flags;
2667 int new_flags;
2668 pos_T namedm[NMARKS];
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002669 visualinfo_T visualinfo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002670 int empty_buffer; /* buffer became empty */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002671 u_header_T *curhead = curbuf->b_u_curhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002672
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002673#ifdef FEAT_AUTOCMD
2674 /* Don't want autocommands using the undo structures here, they are
2675 * invalid till the end. */
2676 block_autocmds();
2677#endif
2678
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002679#ifdef U_DEBUG
2680 u_check(FALSE);
2681#endif
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002682 old_flags = curhead->uh_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002683 new_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
2684 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
2685 setpcmark();
2686
2687 /*
2688 * save marks before undo/redo
2689 */
2690 mch_memmove(namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002691 visualinfo = curbuf->b_visual;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002692 curbuf->b_op_start.lnum = curbuf->b_ml.ml_line_count;
2693 curbuf->b_op_start.col = 0;
2694 curbuf->b_op_end.lnum = 0;
2695 curbuf->b_op_end.col = 0;
2696
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002697 for (uep = curhead->uh_entry; uep != NULL; uep = nuep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002698 {
2699 top = uep->ue_top;
2700 bot = uep->ue_bot;
2701 if (bot == 0)
2702 bot = curbuf->b_ml.ml_line_count + 1;
2703 if (top > curbuf->b_ml.ml_line_count || top >= bot
2704 || bot > curbuf->b_ml.ml_line_count + 1)
2705 {
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002706#ifdef FEAT_AUTOCMD
2707 unblock_autocmds();
2708#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002709 EMSG(_("E438: u_undo: line numbers wrong"));
2710 changed(); /* don't want UNCHANGED now */
2711 return;
2712 }
2713
2714 oldsize = bot - top - 1; /* number of lines before undo */
2715 newsize = uep->ue_size; /* number of lines after undo */
2716
2717 if (top < newlnum)
2718 {
2719 /* If the saved cursor is somewhere in this undo block, move it to
2720 * the remembered position. Makes "gwap" put the cursor back
2721 * where it was. */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002722 lnum = curhead->uh_cursor.lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002723 if (lnum >= top && lnum <= top + newsize + 1)
2724 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002725 curwin->w_cursor = curhead->uh_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002726 newlnum = curwin->w_cursor.lnum - 1;
2727 }
2728 else
2729 {
2730 /* Use the first line that actually changed. Avoids that
2731 * undoing auto-formatting puts the cursor in the previous
2732 * line. */
2733 for (i = 0; i < newsize && i < oldsize; ++i)
2734 if (STRCMP(uep->ue_array[i], ml_get(top + 1 + i)) != 0)
2735 break;
2736 if (i == newsize && newlnum == MAXLNUM && uep->ue_next == NULL)
2737 {
2738 newlnum = top;
2739 curwin->w_cursor.lnum = newlnum + 1;
2740 }
2741 else if (i < newsize)
2742 {
2743 newlnum = top + i;
2744 curwin->w_cursor.lnum = newlnum + 1;
2745 }
2746 }
2747 }
2748
2749 empty_buffer = FALSE;
2750
2751 /* delete the lines between top and bot and save them in newarray */
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002752 if (oldsize > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002753 {
Bram Moolenaar26a60b42005-02-22 08:49:11 +00002754 if ((newarray = (char_u **)U_ALLOC_LINE(
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002755 sizeof(char_u *) * oldsize)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002756 {
2757 do_outofmem_msg((long_u)(sizeof(char_u *) * oldsize));
2758 /*
2759 * We have messed up the entry list, repair is impossible.
2760 * we have to free the rest of the list.
2761 */
2762 while (uep != NULL)
2763 {
2764 nuep = uep->ue_next;
2765 u_freeentry(uep, uep->ue_size);
2766 uep = nuep;
2767 }
2768 break;
2769 }
2770 /* delete backwards, it goes faster in most cases */
2771 for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum)
2772 {
2773 /* what can we do when we run out of memory? */
2774 if ((newarray[i] = u_save_line(lnum)) == NULL)
2775 do_outofmem_msg((long_u)0);
2776 /* remember we deleted the last line in the buffer, and a
2777 * dummy empty line will be inserted */
2778 if (curbuf->b_ml.ml_line_count == 1)
2779 empty_buffer = TRUE;
2780 ml_delete(lnum, FALSE);
2781 }
2782 }
Bram Moolenaar8d343302005-07-12 22:46:17 +00002783 else
2784 newarray = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002785
2786 /* insert the lines in u_array between top and bot */
2787 if (newsize)
2788 {
2789 for (lnum = top, i = 0; i < newsize; ++i, ++lnum)
2790 {
2791 /*
2792 * If the file is empty, there is an empty line 1 that we
2793 * should get rid of, by replacing it with the new line
2794 */
2795 if (empty_buffer && lnum == 0)
2796 ml_replace((linenr_T)1, uep->ue_array[i], TRUE);
2797 else
2798 ml_append(lnum, uep->ue_array[i], (colnr_T)0, FALSE);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002799 vim_free(uep->ue_array[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002800 }
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02002801 vim_free((char_u *)uep->ue_array);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002802 }
2803
2804 /* adjust marks */
2805 if (oldsize != newsize)
2806 {
2807 mark_adjust(top + 1, top + oldsize, (long)MAXLNUM,
2808 (long)newsize - (long)oldsize);
2809 if (curbuf->b_op_start.lnum > top + oldsize)
2810 curbuf->b_op_start.lnum += newsize - oldsize;
2811 if (curbuf->b_op_end.lnum > top + oldsize)
2812 curbuf->b_op_end.lnum += newsize - oldsize;
2813 }
2814
2815 changed_lines(top + 1, 0, bot, newsize - oldsize);
2816
2817 /* set '[ and '] mark */
2818 if (top + 1 < curbuf->b_op_start.lnum)
2819 curbuf->b_op_start.lnum = top + 1;
2820 if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum)
2821 curbuf->b_op_end.lnum = top + 1;
2822 else if (top + newsize > curbuf->b_op_end.lnum)
2823 curbuf->b_op_end.lnum = top + newsize;
2824
2825 u_newcount += newsize;
2826 u_oldcount += oldsize;
2827 uep->ue_size = oldsize;
2828 uep->ue_array = newarray;
2829 uep->ue_bot = top + newsize + 1;
2830
2831 /*
2832 * insert this entry in front of the new entry list
2833 */
2834 nuep = uep->ue_next;
2835 uep->ue_next = newlist;
2836 newlist = uep;
2837 }
2838
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002839 curhead->uh_entry = newlist;
2840 curhead->uh_flags = new_flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002841 if ((old_flags & UH_EMPTYBUF) && bufempty())
2842 curbuf->b_ml.ml_flags |= ML_EMPTY;
2843 if (old_flags & UH_CHANGED)
2844 changed();
2845 else
Bram Moolenaar009b2592004-10-24 19:18:58 +00002846#ifdef FEAT_NETBEANS_INTG
2847 /* per netbeans undo rules, keep it as modified */
2848 if (!isNetbeansModified(curbuf))
2849#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002850 unchanged(curbuf, FALSE);
2851
2852 /*
2853 * restore marks from before undo/redo
2854 */
2855 for (i = 0; i < NMARKS; ++i)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002856 if (curhead->uh_namedm[i].lnum != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002857 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002858 curbuf->b_namedm[i] = curhead->uh_namedm[i];
2859 curhead->uh_namedm[i] = namedm[i];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002860 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002861 if (curhead->uh_visual.vi_start.lnum != 0)
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002862 {
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002863 curbuf->b_visual = curhead->uh_visual;
2864 curhead->uh_visual = visualinfo;
Bram Moolenaara23ccb82006-02-27 00:08:02 +00002865 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002866
2867 /*
2868 * If the cursor is only off by one line, put it at the same position as
2869 * before starting the change (for the "o" command).
2870 * Otherwise the cursor should go to the first undone line.
2871 */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002872 if (curhead->uh_cursor.lnum + 1 == curwin->w_cursor.lnum
Bram Moolenaar071d4272004-06-13 20:20:40 +00002873 && curwin->w_cursor.lnum > 1)
2874 --curwin->w_cursor.lnum;
Bram Moolenaar0390ded2010-08-07 12:54:12 +02002875 if (curwin->w_cursor.lnum <= curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002876 {
Bram Moolenaar0390ded2010-08-07 12:54:12 +02002877 if (curhead->uh_cursor.lnum == curwin->w_cursor.lnum)
2878 {
2879 curwin->w_cursor.col = curhead->uh_cursor.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002880#ifdef FEAT_VIRTUALEDIT
Bram Moolenaar0390ded2010-08-07 12:54:12 +02002881 if (virtual_active() && curhead->uh_cursor_vcol >= 0)
2882 coladvance((colnr_T)curhead->uh_cursor_vcol);
2883 else
2884 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002885#endif
Bram Moolenaar0390ded2010-08-07 12:54:12 +02002886 }
2887 else
2888 beginline(BL_SOL | BL_FIX);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002889 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002890 else
2891 {
2892 /* We get here with the current cursor line being past the end (eg
2893 * after adding lines at the end of the file, and then undoing it).
2894 * check_cursor() will move the cursor to the last line. Move it to
2895 * the first column here. */
2896 curwin->w_cursor.col = 0;
2897#ifdef FEAT_VIRTUALEDIT
2898 curwin->w_cursor.coladd = 0;
2899#endif
2900 }
2901
2902 /* Make sure the cursor is on an existing line and column. */
2903 check_cursor();
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00002904
2905 /* Remember where we are for "g-" and ":earlier 10s". */
2906 curbuf->b_u_seq_cur = curhead->uh_seq;
Bram Moolenaarca003e12006-03-17 23:19:38 +00002907 if (undo)
2908 /* We are below the previous undo. However, to make ":earlier 1s"
2909 * work we compute this as being just above the just undone change. */
2910 --curbuf->b_u_seq_cur;
2911
Bram Moolenaar730cde92010-06-27 05:18:54 +02002912 /* Remember where we are for ":earlier 1f" and ":later 1f". */
2913 if (curhead->uh_save_nr != 0)
2914 {
2915 if (undo)
2916 curbuf->b_u_save_nr_cur = curhead->uh_save_nr - 1;
2917 else
2918 curbuf->b_u_save_nr_cur = curhead->uh_save_nr;
2919 }
2920
Bram Moolenaarca003e12006-03-17 23:19:38 +00002921 /* The timestamp can be the same for multiple changes, just use the one of
2922 * the undone/redone change. */
Bram Moolenaara800b422010-06-27 01:15:55 +02002923 curbuf->b_u_time_cur = curhead->uh_time;
Bram Moolenaarb0b50882010-07-07 18:26:28 +02002924
2925#ifdef FEAT_AUTOCMD
2926 unblock_autocmds();
2927#endif
Bram Moolenaarfecb6602007-10-01 20:54:15 +00002928#ifdef U_DEBUG
2929 u_check(FALSE);
2930#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002931}
2932
2933/*
2934 * If we deleted or added lines, report the number of less/more lines.
2935 * Otherwise, report the number of changes (this may be incorrect
2936 * in some cases, but it's better than nothing).
2937 */
2938 static void
Bram Moolenaardb552d602006-03-23 22:59:57 +00002939u_undo_end(did_undo, absolute)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002940 int did_undo; /* just did an undo */
Bram Moolenaardb552d602006-03-23 22:59:57 +00002941 int absolute; /* used ":undo N" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002942{
Bram Moolenaar89d40322006-08-29 15:30:07 +00002943 char *msgstr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002944 u_header_T *uhp;
2945 char_u msgbuf[80];
Bram Moolenaar1e607892006-03-13 22:15:53 +00002946
Bram Moolenaar071d4272004-06-13 20:20:40 +00002947#ifdef FEAT_FOLDING
2948 if ((fdo_flags & FDO_UNDO) && KeyTyped)
2949 foldOpenCursor();
2950#endif
Bram Moolenaar1e607892006-03-13 22:15:53 +00002951
2952 if (global_busy /* no messages now, wait until global is finished */
2953 || !messaging()) /* 'lazyredraw' set, don't do messages now */
2954 return;
2955
2956 if (curbuf->b_ml.ml_flags & ML_EMPTY)
2957 --u_newcount;
2958
2959 u_oldcount -= u_newcount;
2960 if (u_oldcount == -1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002961 msgstr = N_("more line");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002962 else if (u_oldcount < 0)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002963 msgstr = N_("more lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002964 else if (u_oldcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002965 msgstr = N_("line less");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002966 else if (u_oldcount > 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002967 msgstr = N_("fewer lines");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002968 else
2969 {
2970 u_oldcount = u_newcount;
2971 if (u_newcount == 1)
Bram Moolenaar89d40322006-08-29 15:30:07 +00002972 msgstr = N_("change");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002973 else
Bram Moolenaar89d40322006-08-29 15:30:07 +00002974 msgstr = N_("changes");
Bram Moolenaar1e607892006-03-13 22:15:53 +00002975 }
2976
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002977 if (curbuf->b_u_curhead != NULL)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002978 {
Bram Moolenaardb552d602006-03-23 22:59:57 +00002979 /* For ":undo N" we prefer a "after #N" message. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002980 if (absolute && curbuf->b_u_curhead->uh_next.ptr != NULL)
Bram Moolenaardb552d602006-03-23 22:59:57 +00002981 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002982 uhp = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaardb552d602006-03-23 22:59:57 +00002983 did_undo = FALSE;
2984 }
2985 else if (did_undo)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002986 uhp = curbuf->b_u_curhead;
2987 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02002988 uhp = curbuf->b_u_curhead->uh_next.ptr;
Bram Moolenaar433f7c82006-03-21 21:29:36 +00002989 }
Bram Moolenaar1e607892006-03-13 22:15:53 +00002990 else
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002991 uhp = curbuf->b_u_newhead;
Bram Moolenaar1e607892006-03-13 22:15:53 +00002992
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00002993 if (uhp == NULL)
2994 *msgbuf = NUL;
2995 else
2996 u_add_time(msgbuf, sizeof(msgbuf), uhp->uh_time);
2997
Bram Moolenaarb2c03502010-07-02 20:20:09 +02002998#ifdef FEAT_CONCEAL
2999 {
3000 win_T *wp;
3001
3002 FOR_ALL_WINDOWS(wp)
3003 {
Bram Moolenaarf5963f72010-07-23 22:10:27 +02003004 if (wp->w_buffer == curbuf && wp->w_p_cole > 0)
Bram Moolenaarb2c03502010-07-02 20:20:09 +02003005 redraw_win_later(wp, NOT_VALID);
3006 }
3007 }
3008#endif
3009
Bram Moolenaar433f7c82006-03-21 21:29:36 +00003010 smsg((char_u *)_("%ld %s; %s #%ld %s"),
Bram Moolenaarca003e12006-03-17 23:19:38 +00003011 u_oldcount < 0 ? -u_oldcount : u_oldcount,
Bram Moolenaar89d40322006-08-29 15:30:07 +00003012 _(msgstr),
Bram Moolenaar433f7c82006-03-21 21:29:36 +00003013 did_undo ? _("before") : _("after"),
3014 uhp == NULL ? 0L : uhp->uh_seq,
3015 msgbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003016}
3017
3018/*
3019 * u_sync: stop adding to the current entry list
3020 */
3021 void
Bram Moolenaar779b74b2006-04-10 14:55:34 +00003022u_sync(force)
3023 int force; /* Also sync when no_u_sync is set. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003024{
Bram Moolenaar779b74b2006-04-10 14:55:34 +00003025 /* Skip it when already synced or syncing is disabled. */
3026 if (curbuf->b_u_synced || (!force && no_u_sync > 0))
3027 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003028#if defined(FEAT_XIM) && defined(FEAT_GUI_GTK)
3029 if (im_is_preediting())
3030 return; /* XIM is busy, don't break an undo sequence */
3031#endif
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +01003032 if (get_undolevel() < 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003033 curbuf->b_u_synced = TRUE; /* no entries, nothing to do */
3034 else
3035 {
3036 u_getbot(); /* compute ue_bot of previous u_save */
3037 curbuf->b_u_curhead = NULL;
3038 }
3039}
3040
3041/*
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003042 * ":undolist": List the leafs of the undo tree
3043 */
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003044 void
3045ex_undolist(eap)
Bram Moolenaarfff2bee2010-05-15 13:56:02 +02003046 exarg_T *eap UNUSED;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003047{
3048 garray_T ga;
3049 u_header_T *uhp;
3050 int mark;
3051 int nomark;
3052 int changes = 1;
3053 int i;
3054
3055 /*
3056 * 1: walk the tree to find all leafs, put the info in "ga".
3057 * 2: sort the lines
3058 * 3: display the list
3059 */
3060 mark = ++lastmark;
3061 nomark = ++lastmark;
3062 ga_init2(&ga, (int)sizeof(char *), 20);
3063
3064 uhp = curbuf->b_u_oldhead;
3065 while (uhp != NULL)
3066 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003067 if (uhp->uh_prev.ptr == NULL && uhp->uh_walk != nomark
Bram Moolenaarca003e12006-03-17 23:19:38 +00003068 && uhp->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003069 {
3070 if (ga_grow(&ga, 1) == FAIL)
3071 break;
3072 vim_snprintf((char *)IObuff, IOSIZE, "%6ld %7ld ",
3073 uhp->uh_seq, changes);
3074 u_add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff),
3075 uhp->uh_time);
Bram Moolenaara800b422010-06-27 01:15:55 +02003076 if (uhp->uh_save_nr > 0)
3077 {
Bram Moolenaardba01a02010-11-03 19:32:42 +01003078 while (STRLEN(IObuff) < 33)
Bram Moolenaara800b422010-06-27 01:15:55 +02003079 STRCAT(IObuff, " ");
3080 vim_snprintf_add((char *)IObuff, IOSIZE,
3081 " %3ld", uhp->uh_save_nr);
3082 }
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003083 ((char_u **)(ga.ga_data))[ga.ga_len++] = vim_strsave(IObuff);
3084 }
3085
3086 uhp->uh_walk = mark;
3087
3088 /* go down in the tree if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003089 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
3090 && uhp->uh_prev.ptr->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003091 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003092 uhp = uhp->uh_prev.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003093 ++changes;
3094 }
3095
3096 /* go to alternate branch if we haven't been there */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003097 else if (uhp->uh_alt_next.ptr != NULL
3098 && uhp->uh_alt_next.ptr->uh_walk != nomark
3099 && uhp->uh_alt_next.ptr->uh_walk != mark)
3100 uhp = uhp->uh_alt_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003101
3102 /* go up in the tree if we haven't been there and we are at the
3103 * start of alternate branches */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003104 else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
3105 && uhp->uh_next.ptr->uh_walk != nomark
3106 && uhp->uh_next.ptr->uh_walk != mark)
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003107 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003108 uhp = uhp->uh_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003109 --changes;
3110 }
3111
3112 else
3113 {
3114 /* need to backtrack; mark this node as done */
3115 uhp->uh_walk = nomark;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003116 if (uhp->uh_alt_prev.ptr != NULL)
3117 uhp = uhp->uh_alt_prev.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003118 else
3119 {
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003120 uhp = uhp->uh_next.ptr;
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003121 --changes;
3122 }
3123 }
3124 }
3125
3126 if (ga.ga_len == 0)
3127 MSG(_("Nothing to undo"));
3128 else
3129 {
3130 sort_strings((char_u **)ga.ga_data, ga.ga_len);
3131
3132 msg_start();
Bram Moolenaardba01a02010-11-03 19:32:42 +01003133 msg_puts_attr((char_u *)_("number changes when saved"),
Bram Moolenaara800b422010-06-27 01:15:55 +02003134 hl_attr(HLF_T));
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003135 for (i = 0; i < ga.ga_len && !got_int; ++i)
3136 {
3137 msg_putchar('\n');
3138 if (got_int)
3139 break;
3140 msg_puts(((char_u **)ga.ga_data)[i]);
3141 }
3142 msg_end();
3143
3144 ga_clear_strings(&ga);
3145 }
3146}
3147
3148/*
3149 * Put the timestamp of an undo header in "buf[buflen]" in a nice format.
3150 */
3151 static void
3152u_add_time(buf, buflen, tt)
3153 char_u *buf;
3154 size_t buflen;
3155 time_t tt;
3156{
3157#ifdef HAVE_STRFTIME
3158 struct tm *curtime;
3159
3160 if (time(NULL) - tt >= 100)
3161 {
3162 curtime = localtime(&tt);
Bram Moolenaardba01a02010-11-03 19:32:42 +01003163 if (time(NULL) - tt < (60L * 60L * 12L))
3164 /* within 12 hours */
3165 (void)strftime((char *)buf, buflen, "%H:%M:%S", curtime);
Bram Moolenaardba01a02010-11-03 19:32:42 +01003166 else
Bram Moolenaarb9ce83e2012-08-23 12:59:02 +02003167 /* longer ago */
Bram Moolenaar5e3d6ca2011-01-22 21:25:11 +01003168 (void)strftime((char *)buf, buflen, "%Y/%m/%d %H:%M:%S", curtime);
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003169 }
3170 else
3171#endif
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003172 vim_snprintf((char *)buf, buflen, _("%ld seconds ago"),
Bram Moolenaarefd2bf12006-03-16 21:41:35 +00003173 (long)(time(NULL) - tt));
3174}
3175
3176/*
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003177 * ":undojoin": continue adding to the last entry list
3178 */
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003179 void
3180ex_undojoin(eap)
Bram Moolenaarfff2bee2010-05-15 13:56:02 +02003181 exarg_T *eap UNUSED;
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003182{
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003183 if (curbuf->b_u_newhead == NULL)
3184 return; /* nothing changed before */
Bram Moolenaar57657d82006-04-21 22:12:41 +00003185 if (curbuf->b_u_curhead != NULL)
3186 {
3187 EMSG(_("E790: undojoin is not allowed after undo"));
3188 return;
3189 }
3190 if (!curbuf->b_u_synced)
3191 return; /* already unsynced */
Bram Moolenaarf5a2fd82013-11-06 05:26:15 +01003192 if (get_undolevel() < 0)
Bram Moolenaare224ffa2006-03-01 00:01:28 +00003193 return; /* no entries, nothing to do */
3194 else
3195 {
3196 /* Go back to the last entry */
3197 curbuf->b_u_curhead = curbuf->b_u_newhead;
3198 curbuf->b_u_synced = FALSE; /* no entries, nothing to do */
3199 }
3200}
3201
3202/*
Bram Moolenaar59f931e2010-07-24 20:27:03 +02003203 * Called after writing or reloading the file and setting b_changed to FALSE.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003204 * Now an undo means that the buffer is modified.
3205 */
3206 void
3207u_unchanged(buf)
3208 buf_T *buf;
3209{
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003210 u_unch_branch(buf->b_u_oldhead);
3211 buf->b_did_warn = FALSE;
3212}
3213
Bram Moolenaar730cde92010-06-27 05:18:54 +02003214/*
Bram Moolenaarf9bb7342010-08-04 14:29:54 +02003215 * After reloading a buffer which was saved for 'undoreload': Find the first
3216 * line that was changed and set the cursor there.
3217 */
3218 void
3219u_find_first_changed()
3220{
3221 u_header_T *uhp = curbuf->b_u_newhead;
3222 u_entry_T *uep;
3223 linenr_T lnum;
3224
3225 if (curbuf->b_u_curhead != NULL || uhp == NULL)
3226 return; /* undid something in an autocmd? */
3227
3228 /* Check that the last undo block was for the whole file. */
3229 uep = uhp->uh_entry;
3230 if (uep->ue_top != 0 || uep->ue_bot != 0)
3231 return;
3232
3233 for (lnum = 1; lnum < curbuf->b_ml.ml_line_count
3234 && lnum <= uep->ue_size; ++lnum)
3235 if (STRCMP(ml_get_buf(curbuf, lnum, FALSE),
3236 uep->ue_array[lnum - 1]) != 0)
3237 {
3238 clearpos(&(uhp->uh_cursor));
3239 uhp->uh_cursor.lnum = lnum;
3240 return;
3241 }
3242 if (curbuf->b_ml.ml_line_count != uep->ue_size)
3243 {
3244 /* lines added or deleted at the end, put the cursor there */
3245 clearpos(&(uhp->uh_cursor));
3246 uhp->uh_cursor.lnum = lnum;
3247 }
3248}
3249
3250/*
Bram Moolenaar730cde92010-06-27 05:18:54 +02003251 * Increase the write count, store it in the last undo header, what would be
3252 * used for "u".
3253 */
3254 void
3255u_update_save_nr(buf)
3256 buf_T *buf;
3257{
3258 u_header_T *uhp;
3259
3260 ++buf->b_u_save_nr_last;
3261 buf->b_u_save_nr_cur = buf->b_u_save_nr_last;
3262 uhp = buf->b_u_curhead;
3263 if (uhp != NULL)
3264 uhp = uhp->uh_next.ptr;
3265 else
3266 uhp = buf->b_u_newhead;
3267 if (uhp != NULL)
3268 uhp->uh_save_nr = buf->b_u_save_nr_last;
3269}
3270
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003271 static void
3272u_unch_branch(uhp)
3273 u_header_T *uhp;
3274{
Bram Moolenaar1e607892006-03-13 22:15:53 +00003275 u_header_T *uh;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003276
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003277 for (uh = uhp; uh != NULL; uh = uh->uh_prev.ptr)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003278 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003279 uh->uh_flags |= UH_CHANGED;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003280 if (uh->uh_alt_next.ptr != NULL)
3281 u_unch_branch(uh->uh_alt_next.ptr); /* recursive */
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003282 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003283}
3284
3285/*
3286 * Get pointer to last added entry.
3287 * If it's not valid, give an error message and return NULL.
3288 */
3289 static u_entry_T *
3290u_get_headentry()
3291{
3292 if (curbuf->b_u_newhead == NULL || curbuf->b_u_newhead->uh_entry == NULL)
3293 {
3294 EMSG(_("E439: undo list corrupt"));
3295 return NULL;
3296 }
3297 return curbuf->b_u_newhead->uh_entry;
3298}
3299
3300/*
3301 * u_getbot(): compute the line number of the previous u_save
3302 * It is called only when b_u_synced is FALSE.
3303 */
3304 static void
3305u_getbot()
3306{
3307 u_entry_T *uep;
3308 linenr_T extra;
3309
3310 uep = u_get_headentry(); /* check for corrupt undo list */
3311 if (uep == NULL)
3312 return;
3313
3314 uep = curbuf->b_u_newhead->uh_getbot_entry;
3315 if (uep != NULL)
3316 {
3317 /*
3318 * the new ue_bot is computed from the number of lines that has been
3319 * inserted (0 - deleted) since calling u_save. This is equal to the
3320 * old line count subtracted from the current line count.
3321 */
3322 extra = curbuf->b_ml.ml_line_count - uep->ue_lcount;
3323 uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra;
3324 if (uep->ue_bot < 1 || uep->ue_bot > curbuf->b_ml.ml_line_count)
3325 {
3326 EMSG(_("E440: undo line missing"));
3327 uep->ue_bot = uep->ue_top + 1; /* assume all lines deleted, will
3328 * get all the old lines back
3329 * without deleting the current
3330 * ones */
3331 }
3332
3333 curbuf->b_u_newhead->uh_getbot_entry = NULL;
3334 }
3335
3336 curbuf->b_u_synced = TRUE;
3337}
3338
3339/*
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003340 * Free one header "uhp" and its entry list and adjust the pointers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003341 */
3342 static void
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003343u_freeheader(buf, uhp, uhpp)
Bram Moolenaar26a60b42005-02-22 08:49:11 +00003344 buf_T *buf;
Bram Moolenaar1e607892006-03-13 22:15:53 +00003345 u_header_T *uhp;
3346 u_header_T **uhpp; /* if not NULL reset when freeing this header */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003347{
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003348 u_header_T *uhap;
3349
Bram Moolenaar1e607892006-03-13 22:15:53 +00003350 /* When there is an alternate redo list free that branch completely,
3351 * because we can never go there. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003352 if (uhp->uh_alt_next.ptr != NULL)
3353 u_freebranch(buf, uhp->uh_alt_next.ptr, uhpp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003354
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003355 if (uhp->uh_alt_prev.ptr != NULL)
3356 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003357
Bram Moolenaar1e607892006-03-13 22:15:53 +00003358 /* Update the links in the list to remove the header. */
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003359 if (uhp->uh_next.ptr == NULL)
3360 buf->b_u_oldhead = uhp->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003361 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003362 uhp->uh_next.ptr->uh_prev.ptr = uhp->uh_prev.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003363
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003364 if (uhp->uh_prev.ptr == NULL)
3365 buf->b_u_newhead = uhp->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003366 else
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003367 for (uhap = uhp->uh_prev.ptr; uhap != NULL;
3368 uhap = uhap->uh_alt_next.ptr)
3369 uhap->uh_next.ptr = uhp->uh_next.ptr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003370
Bram Moolenaar1e607892006-03-13 22:15:53 +00003371 u_freeentries(buf, uhp, uhpp);
3372}
3373
3374/*
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003375 * Free an alternate branch and any following alternate branches.
Bram Moolenaar1e607892006-03-13 22:15:53 +00003376 */
3377 static void
3378u_freebranch(buf, uhp, uhpp)
3379 buf_T *buf;
3380 u_header_T *uhp;
3381 u_header_T **uhpp; /* if not NULL reset when freeing this header */
3382{
3383 u_header_T *tofree, *next;
3384
Bram Moolenaar07d06772007-11-10 21:51:15 +00003385 /* If this is the top branch we may need to use u_freeheader() to update
3386 * all the pointers. */
3387 if (uhp == buf->b_u_oldhead)
3388 {
Bram Moolenaaraa887322013-11-07 03:04:11 +01003389 while (buf->b_u_oldhead != NULL)
3390 u_freeheader(buf, buf->b_u_oldhead, uhpp);
Bram Moolenaar07d06772007-11-10 21:51:15 +00003391 return;
3392 }
3393
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003394 if (uhp->uh_alt_prev.ptr != NULL)
3395 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +00003396
3397 next = uhp;
3398 while (next != NULL)
3399 {
3400 tofree = next;
Bram Moolenaar83d09bb2010-06-01 19:58:08 +02003401 if (tofree->uh_alt_next.ptr != NULL)
3402 u_freebranch(buf, tofree->uh_alt_next.ptr, uhpp); /* recursive */
3403 next = tofree->uh_prev.ptr;
Bram Moolenaar1e607892006-03-13 22:15:53 +00003404 u_freeentries(buf, tofree, uhpp);
3405 }
3406}
3407
3408/*
3409 * Free all the undo entries for one header and the header itself.
3410 * This means that "uhp" is invalid when returning.
3411 */
3412 static void
3413u_freeentries(buf, uhp, uhpp)
3414 buf_T *buf;
3415 u_header_T *uhp;
3416 u_header_T **uhpp; /* if not NULL reset when freeing this header */
3417{
3418 u_entry_T *uep, *nuep;
3419
3420 /* Check for pointers to the header that become invalid now. */
3421 if (buf->b_u_curhead == uhp)
3422 buf->b_u_curhead = NULL;
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003423 if (buf->b_u_newhead == uhp)
3424 buf->b_u_newhead = NULL; /* freeing the newest entry */
Bram Moolenaar1e607892006-03-13 22:15:53 +00003425 if (uhpp != NULL && uhp == *uhpp)
3426 *uhpp = NULL;
3427
3428 for (uep = uhp->uh_entry; uep != NULL; uep = nuep)
3429 {
3430 nuep = uep->ue_next;
3431 u_freeentry(uep, uep->ue_size);
3432 }
3433
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003434#ifdef U_DEBUG
3435 uhp->uh_magic = 0;
3436#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003437 vim_free((char_u *)uhp);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00003438 --buf->b_u_numhead;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003439}
3440
3441/*
3442 * free entry 'uep' and 'n' lines in uep->ue_array[]
3443 */
3444 static void
3445u_freeentry(uep, n)
3446 u_entry_T *uep;
3447 long n;
3448{
Bram Moolenaar8d343302005-07-12 22:46:17 +00003449 while (n > 0)
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003450 vim_free(uep->ue_array[--n]);
3451 vim_free((char_u *)uep->ue_array);
Bram Moolenaarfecb6602007-10-01 20:54:15 +00003452#ifdef U_DEBUG
3453 uep->ue_magic = 0;
3454#endif
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003455 vim_free((char_u *)uep);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003456}
3457
3458/*
3459 * invalidate the undo buffer; called when storage has already been released
3460 */
3461 void
3462u_clearall(buf)
3463 buf_T *buf;
3464{
3465 buf->b_u_newhead = buf->b_u_oldhead = buf->b_u_curhead = NULL;
3466 buf->b_u_synced = TRUE;
3467 buf->b_u_numhead = 0;
3468 buf->b_u_line_ptr = NULL;
3469 buf->b_u_line_lnum = 0;
3470}
3471
3472/*
3473 * save the line "lnum" for the "U" command
3474 */
3475 void
3476u_saveline(lnum)
3477 linenr_T lnum;
3478{
3479 if (lnum == curbuf->b_u_line_lnum) /* line is already saved */
3480 return;
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00003481 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) /* should never happen */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003482 return;
3483 u_clearline();
3484 curbuf->b_u_line_lnum = lnum;
3485 if (curwin->w_cursor.lnum == lnum)
3486 curbuf->b_u_line_colnr = curwin->w_cursor.col;
3487 else
3488 curbuf->b_u_line_colnr = 0;
3489 if ((curbuf->b_u_line_ptr = u_save_line(lnum)) == NULL)
3490 do_outofmem_msg((long_u)0);
3491}
3492
3493/*
3494 * clear the line saved for the "U" command
3495 * (this is used externally for crossing a line while in insert mode)
3496 */
3497 void
3498u_clearline()
3499{
3500 if (curbuf->b_u_line_ptr != NULL)
3501 {
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003502 vim_free(curbuf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003503 curbuf->b_u_line_ptr = NULL;
3504 curbuf->b_u_line_lnum = 0;
3505 }
3506}
3507
3508/*
3509 * Implementation of the "U" command.
3510 * Differentiation from vi: "U" can be undone with the next "U".
3511 * We also allow the cursor to be in another line.
Bram Moolenaard04b7502010-07-08 22:27:55 +02003512 * Careful: may trigger autocommands that reload the buffer.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003513 */
3514 void
3515u_undoline()
3516{
3517 colnr_T t;
3518 char_u *oldp;
3519
3520 if (undo_off)
3521 return;
3522
Bram Moolenaare3300c82008-02-13 14:21:38 +00003523 if (curbuf->b_u_line_ptr == NULL
3524 || curbuf->b_u_line_lnum > curbuf->b_ml.ml_line_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003525 {
3526 beep_flush();
3527 return;
3528 }
Bram Moolenaare3300c82008-02-13 14:21:38 +00003529
3530 /* first save the line for the 'u' command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003531 if (u_savecommon(curbuf->b_u_line_lnum - 1,
Bram Moolenaar59f931e2010-07-24 20:27:03 +02003532 curbuf->b_u_line_lnum + 1, (linenr_T)0, FALSE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003533 return;
3534 oldp = u_save_line(curbuf->b_u_line_lnum);
3535 if (oldp == NULL)
3536 {
3537 do_outofmem_msg((long_u)0);
3538 return;
3539 }
3540 ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, TRUE);
3541 changed_bytes(curbuf->b_u_line_lnum, 0);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003542 vim_free(curbuf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003543 curbuf->b_u_line_ptr = oldp;
3544
3545 t = curbuf->b_u_line_colnr;
3546 if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum)
3547 curbuf->b_u_line_colnr = curwin->w_cursor.col;
3548 curwin->w_cursor.col = t;
3549 curwin->w_cursor.lnum = curbuf->b_u_line_lnum;
Bram Moolenaare3300c82008-02-13 14:21:38 +00003550 check_cursor_col();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003551}
3552
3553/*
Bram Moolenaar26a60b42005-02-22 08:49:11 +00003554 * Free all allocated memory blocks for the buffer 'buf'.
3555 */
3556 void
3557u_blockfree(buf)
3558 buf_T *buf;
3559{
Bram Moolenaar1e607892006-03-13 22:15:53 +00003560 while (buf->b_u_oldhead != NULL)
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00003561 u_freeheader(buf, buf->b_u_oldhead, NULL);
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003562 vim_free(buf->b_u_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003563}
3564
3565/*
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003566 * u_save_line(): allocate memory and copy line 'lnum' into it.
3567 * Returns NULL when out of memory.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003568 */
3569 static char_u *
3570u_save_line(lnum)
3571 linenr_T lnum;
3572{
Bram Moolenaarf05e3b02010-05-29 15:40:47 +02003573 return vim_strsave(ml_get(lnum));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003574}
3575
3576/*
3577 * Check if the 'modified' flag is set, or 'ff' has changed (only need to
3578 * check the first character, because it can only be "dos", "unix" or "mac").
3579 * "nofile" and "scratch" type buffers are considered to always be unchanged.
3580 */
3581 int
3582bufIsChanged(buf)
3583 buf_T *buf;
3584{
3585 return
3586#ifdef FEAT_QUICKFIX
3587 !bt_dontwrite(buf) &&
3588#endif
Bram Moolenaar164c60f2011-01-22 00:11:50 +01003589 (buf->b_changed || file_ff_differs(buf, TRUE));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003590}
3591
3592 int
3593curbufIsChanged()
3594{
3595 return
3596#ifdef FEAT_QUICKFIX
3597 !bt_dontwrite(curbuf) &&
3598#endif
Bram Moolenaar164c60f2011-01-22 00:11:50 +01003599 (curbuf->b_changed || file_ff_differs(curbuf, TRUE));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003600}
Bram Moolenaara800b422010-06-27 01:15:55 +02003601
3602#if defined(FEAT_EVAL) || defined(PROTO)
3603/*
3604 * For undotree(): Append the list of undo blocks at "first_uhp" to "list".
3605 * Recursive.
3606 */
3607 void
3608u_eval_tree(first_uhp, list)
3609 u_header_T *first_uhp;
3610 list_T *list;
3611{
3612 u_header_T *uhp = first_uhp;
3613 dict_T *dict;
3614
3615 while (uhp != NULL)
3616 {
3617 dict = dict_alloc();
3618 if (dict == NULL)
3619 return;
3620 dict_add_nr_str(dict, "seq", uhp->uh_seq, NULL);
Bram Moolenaarb2c03502010-07-02 20:20:09 +02003621 dict_add_nr_str(dict, "time", (long)uhp->uh_time, NULL);
Bram Moolenaara800b422010-06-27 01:15:55 +02003622 if (uhp == curbuf->b_u_newhead)
3623 dict_add_nr_str(dict, "newhead", 1, NULL);
3624 if (uhp == curbuf->b_u_curhead)
3625 dict_add_nr_str(dict, "curhead", 1, NULL);
3626 if (uhp->uh_save_nr > 0)
3627 dict_add_nr_str(dict, "save", uhp->uh_save_nr, NULL);
3628
3629 if (uhp->uh_alt_next.ptr != NULL)
3630 {
3631 list_T *alt_list = list_alloc();
3632
3633 if (alt_list != NULL)
3634 {
3635 /* Recursive call to add alternate undo tree. */
3636 u_eval_tree(uhp->uh_alt_next.ptr, alt_list);
3637 dict_add_list(dict, "alt", alt_list);
3638 }
3639 }
3640
3641 list_append_dict(list, dict);
3642 uhp = uhp->uh_prev.ptr;
3643 }
3644}
3645#endif