blob: c2e1074248994dac2dd97761fd76fe282744196b [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010010// for debugging
11// #define CHECK(c, s) do { if (c) emsg((s)); } while (0)
Bram Moolenaar6f470022018-04-10 18:47:20 +020012#define CHECK(c, s) do { /**/ } while (0)
Bram Moolenaar071d4272004-06-13 20:20:40 +000013
14/*
15 * memline.c: Contains the functions for appending, deleting and changing the
Bram Moolenaar4770d092006-01-12 23:22:24 +000016 * text lines. The memfile functions are used to store the information in
17 * blocks of memory, backed up by a file. The structure of the information is
18 * a tree. The root of the tree is a pointer block. The leaves of the tree
19 * are data blocks. In between may be several layers of pointer blocks,
20 * forming branches.
Bram Moolenaar071d4272004-06-13 20:20:40 +000021 *
22 * Three types of blocks are used:
23 * - Block nr 0 contains information for recovery
24 * - Pointer blocks contain list of pointers to other blocks.
25 * - Data blocks contain the actual text.
26 *
27 * Block nr 0 contains the block0 structure (see below).
28 *
29 * Block nr 1 is the first pointer block. It is the root of the tree.
30 * Other pointer blocks are branches.
31 *
32 * If a line is too big to fit in a single page, the block containing that
33 * line is made big enough to hold the line. It may span several pages.
34 * Otherwise all blocks are one page.
35 *
36 * A data block that was filled when starting to edit a file and was not
37 * changed since then, can have a negative block number. This means that it
38 * has not yet been assigned a place in the file. When recovering, the lines
39 * in this data block can be read from the original file. When the block is
40 * changed (lines appended/deleted/changed) or when it is flushed it gets a
41 * positive number. Use mf_trans_del() to get the new number, before calling
42 * mf_get().
43 */
44
Bram Moolenaar071d4272004-06-13 20:20:40 +000045#include "vim.h"
46
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010047#ifndef UNIX // it's in os_unix.h for Unix
Bram Moolenaar071d4272004-06-13 20:20:40 +000048# include <time.h>
49#endif
50
Christian Brabandtf573c6e2021-06-20 14:02:16 +020051// for randombytes_buf
52#ifdef FEAT_SODIUM
53# include <sodium.h>
54#endif
55
Bram Moolenaar5a6404c2006-11-01 17:12:57 +000056#if defined(SASC) || defined(__amigaos4__)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010057# include <proto/dos.h> // for Open() and Close()
Bram Moolenaar071d4272004-06-13 20:20:40 +000058#endif
59
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010060typedef struct block0 ZERO_BL; // contents of the first block
61typedef struct pointer_block PTR_BL; // contents of a pointer block
62typedef struct data_block DATA_BL; // contents of a data block
63typedef struct pointer_entry PTR_EN; // block/line-count pair
Bram Moolenaar071d4272004-06-13 20:20:40 +000064
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010065#define DATA_ID (('d' << 8) + 'a') // data block id
66#define PTR_ID (('p' << 8) + 't') // pointer block id
67#define BLOCK0_ID0 'b' // block 0 id 0
68#define BLOCK0_ID1 '0' // block 0 id 1
69#define BLOCK0_ID1_C0 'c' // block 0 id 1 'cm' 0
70#define BLOCK0_ID1_C1 'C' // block 0 id 1 'cm' 1
71#define BLOCK0_ID1_C2 'd' // block 0 id 1 'cm' 2
Christian Brabandtf573c6e2021-06-20 14:02:16 +020072#define BLOCK0_ID1_C3 'S' // block 0 id 1 'cm' 3 - but not actually used
Bram Moolenaar8f4ac012014-08-10 13:38:34 +020073
74#if defined(FEAT_CRYPT)
75static int id1_codes[] = {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010076 BLOCK0_ID1_C0, // CRYPT_M_ZIP
77 BLOCK0_ID1_C1, // CRYPT_M_BF
78 BLOCK0_ID1_C2, // CRYPT_M_BF2
Christian Brabandtf573c6e2021-06-20 14:02:16 +020079 BLOCK0_ID1_C3, // CRYPT_M_SOD - Unused!
Bram Moolenaar8f4ac012014-08-10 13:38:34 +020080};
81#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000082
83/*
84 * pointer to a block, used in a pointer block
85 */
86struct pointer_entry
87{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010088 blocknr_T pe_bnum; // block number
89 linenr_T pe_line_count; // number of lines in this branch
90 linenr_T pe_old_lnum; // lnum for this block (for recovery)
91 int pe_page_count; // number of pages in block pe_bnum
Bram Moolenaar071d4272004-06-13 20:20:40 +000092};
93
94/*
95 * A pointer block contains a list of branches in the tree.
96 */
97struct pointer_block
98{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010099 short_u pb_id; // ID for pointer block: PTR_ID
100 short_u pb_count; // number of pointers in this block
101 short_u pb_count_max; // maximum value for pb_count
102 PTR_EN pb_pointer[1]; // list of pointers to blocks (actually longer)
103 // followed by empty space until end of page
Bram Moolenaar071d4272004-06-13 20:20:40 +0000104};
105
106/*
107 * A data block is a leaf in the tree.
108 *
109 * The text of the lines is at the end of the block. The text of the first line
110 * in the block is put at the end, the text of the second line in front of it,
111 * etc. Thus the order of the lines is the opposite of the line number.
112 */
113struct data_block
114{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100115 short_u db_id; // ID for data block: DATA_ID
116 unsigned db_free; // free space available
117 unsigned db_txt_start; // byte where text starts
118 unsigned db_txt_end; // byte just after data block
119 linenr_T db_line_count; // number of lines in this block
120 unsigned db_index[1]; // index for start of line (actually bigger)
Bram Moolenaar4b96df52020-01-26 22:00:26 +0100121 // followed by empty space up to db_txt_start
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100122 // followed by the text in the lines until
123 // end of page
Bram Moolenaar071d4272004-06-13 20:20:40 +0000124};
125
126/*
127 * The low bits of db_index hold the actual index. The topmost bit is
128 * used for the global command to be able to mark a line.
129 * This method is not clean, but otherwise there would be at least one extra
130 * byte used for each line.
131 * The mark has to be in this place to keep it with the correct line when other
132 * lines are inserted or deleted.
133 */
134#define DB_MARKED ((unsigned)1 << ((sizeof(unsigned) * 8) - 1))
135#define DB_INDEX_MASK (~DB_MARKED)
136
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100137#define INDEX_SIZE (sizeof(unsigned)) // size of one db_index entry
138#define HEADER_SIZE (sizeof(DATA_BL) - INDEX_SIZE) // size of data block header
Bram Moolenaar071d4272004-06-13 20:20:40 +0000139
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100140#define B0_FNAME_SIZE_ORG 900 // what it was in older versions
141#define B0_FNAME_SIZE_NOCRYPT 898 // 2 bytes used for other things
142#define B0_FNAME_SIZE_CRYPT 890 // 10 bytes used for other things
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000143#define B0_UNAME_SIZE 40
144#define B0_HNAME_SIZE 40
Bram Moolenaar071d4272004-06-13 20:20:40 +0000145/*
146 * Restrict the numbers to 32 bits, otherwise most compilers will complain.
147 * This won't detect a 64 bit machine that only swaps a byte in the top 32
148 * bits, but that is crazy anyway.
149 */
150#define B0_MAGIC_LONG 0x30313233L
151#define B0_MAGIC_INT 0x20212223L
152#define B0_MAGIC_SHORT 0x10111213L
153#define B0_MAGIC_CHAR 0x55
154
155/*
156 * Block zero holds all info about the swap file.
157 *
158 * NOTE: DEFINITION OF BLOCK 0 SHOULD NOT CHANGE! It would make all existing
159 * swap files unusable!
160 *
161 * If size of block0 changes anyway, adjust MIN_SWAP_PAGE_SIZE in vim.h!!
162 *
Bram Moolenaarbae0c162007-05-10 19:30:25 +0000163 * This block is built up of single bytes, to make it portable across
Bram Moolenaar071d4272004-06-13 20:20:40 +0000164 * different machines. b0_magic_* is used to check the byte order and size of
165 * variables, because the rest of the swap file is not portable.
166 */
167struct block0
168{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100169 char_u b0_id[2]; // id for block 0: BLOCK0_ID0 and BLOCK0_ID1,
170 // BLOCK0_ID1_C0, BLOCK0_ID1_C1, etc.
171 char_u b0_version[10]; // Vim version string
172 char_u b0_page_size[4];// number of bytes per page
173 char_u b0_mtime[4]; // last modification time of file
174 char_u b0_ino[4]; // inode of b0_fname
175 char_u b0_pid[4]; // process id of creator (or 0)
176 char_u b0_uname[B0_UNAME_SIZE]; // name of user (uid if no name)
177 char_u b0_hname[B0_HNAME_SIZE]; // host name (if it has a name)
178 char_u b0_fname[B0_FNAME_SIZE_ORG]; // name of file being edited
179 long b0_magic_long; // check for byte order of long
180 int b0_magic_int; // check for byte order of int
181 short b0_magic_short; // check for byte order of short
182 char_u b0_magic_char; // check for last char
Bram Moolenaar071d4272004-06-13 20:20:40 +0000183};
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000184
185/*
Bram Moolenaar4770d092006-01-12 23:22:24 +0000186 * Note: b0_dirty and b0_flags are put at the end of the file name. For very
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000187 * long file names in older versions of Vim they are invalid.
188 * The 'fileencoding' comes before b0_flags, with a NUL in front. But only
189 * when there is room, for very long file names it's omitted.
190 */
191#define B0_DIRTY 0x55
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200192#define b0_dirty b0_fname[B0_FNAME_SIZE_ORG - 1]
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000193
194/*
195 * The b0_flags field is new in Vim 7.0.
196 */
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200197#define b0_flags b0_fname[B0_FNAME_SIZE_ORG - 2]
198
199/*
200 * Crypt seed goes here, 8 bytes. New in Vim 7.3.
201 * Without encryption these bytes may be used for 'fenc'.
202 */
203#define b0_seed b0_fname[B0_FNAME_SIZE_ORG - 2 - MF_SEED_LEN]
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000204
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100205// The lowest two bits contain the fileformat. Zero means it's not set
206// (compatible with Vim 6.x), otherwise it's EOL_UNIX + 1, EOL_DOS + 1 or
207// EOL_MAC + 1.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000208#define B0_FF_MASK 3
209
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100210// Swap file is in directory of edited file. Used to find the file from
211// different mount points.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000212#define B0_SAME_DIR 4
213
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100214// The 'fileencoding' is at the end of b0_fname[], with a NUL in front of it.
215// When empty there is only the NUL.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000216#define B0_HAS_FENC 8
Bram Moolenaar071d4272004-06-13 20:20:40 +0000217
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100218#define STACK_INCR 5 // nr of entries added to ml_stack at a time
Bram Moolenaar071d4272004-06-13 20:20:40 +0000219
220/*
221 * The line number where the first mark may be is remembered.
222 * If it is 0 there are no marks at all.
223 * (always used for the current buffer only, no buffer change possible while
224 * executing a global command).
225 */
226static linenr_T lowest_marked = 0;
227
228/*
229 * arguments for ml_find_line()
230 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100231#define ML_DELETE 0x11 // delete line
232#define ML_INSERT 0x12 // insert line
233#define ML_FIND 0x13 // just find the line
234#define ML_FLUSH 0x02 // flush locked block
235#define ML_SIMPLE(x) (x & 0x10) // DEL, INS or FIND
Bram Moolenaar071d4272004-06-13 20:20:40 +0000236
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100237// argument for ml_upd_block0()
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200238typedef enum {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100239 UB_FNAME = 0 // update timestamp and filename
240 , UB_SAME_DIR // update the B0_SAME_DIR flag
241 , UB_CRYPT // update crypt key
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200242} upd_block0_T;
243
244#ifdef FEAT_CRYPT
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100245static void ml_set_b0_crypt(buf_T *buf, ZERO_BL *b0p);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200246#endif
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100247static void ml_upd_block0(buf_T *buf, upd_block0_T what);
248static void set_b0_fname(ZERO_BL *, buf_T *buf);
249static void set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100250static void add_b0_fenc(ZERO_BL *b0p, buf_T *buf);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100251static time_t swapfile_info(char_u *);
252static int recov_file_names(char_u **, char_u *, int prepend_dot);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100253static char_u *findswapname(buf_T *, char_u **, char_u *);
254static void ml_flush_line(buf_T *);
255static bhdr_T *ml_new_data(memfile_T *, int, int);
256static bhdr_T *ml_new_ptr(memfile_T *);
257static bhdr_T *ml_find_line(buf_T *, linenr_T, int);
258static int ml_add_stack(buf_T *);
259static void ml_lineadd(buf_T *, int);
260static int b0_magic_wrong(ZERO_BL *);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000261#ifdef CHECK_INODE
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100262static int fnamecmp_ino(char_u *, char_u *, long);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000263#endif
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100264static void long_to_char(long, char_u *);
265static long char_to_long(char_u *);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200266#ifdef FEAT_CRYPT
Bram Moolenaar8767f522016-07-01 17:17:39 +0200267static cryptstate_T *ml_crypt_prepare(memfile_T *mfp, off_T offset, int reading);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200268#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000269#ifdef FEAT_BYTEOFF
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100270static void ml_updatechunk(buf_T *buf, long line, long len, int updtype);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000271#endif
272
273/*
Bram Moolenaar4770d092006-01-12 23:22:24 +0000274 * Open a new memline for "buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +0000275 *
Bram Moolenaar4770d092006-01-12 23:22:24 +0000276 * Return FAIL for failure, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000277 */
278 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100279ml_open(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000280{
281 memfile_T *mfp;
282 bhdr_T *hp = NULL;
283 ZERO_BL *b0p;
284 PTR_BL *pp;
285 DATA_BL *dp;
286
Bram Moolenaar4770d092006-01-12 23:22:24 +0000287 /*
288 * init fields in memline struct
289 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100290 buf->b_ml.ml_stack_size = 0; // no stack yet
291 buf->b_ml.ml_stack = NULL; // no stack yet
292 buf->b_ml.ml_stack_top = 0; // nothing in the stack
293 buf->b_ml.ml_locked = NULL; // no cached block
294 buf->b_ml.ml_line_lnum = 0; // no cached line
Bram Moolenaar071d4272004-06-13 20:20:40 +0000295#ifdef FEAT_BYTEOFF
Bram Moolenaar4770d092006-01-12 23:22:24 +0000296 buf->b_ml.ml_chunksize = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000297#endif
298
Bram Moolenaare1004402020-10-24 20:49:43 +0200299 if (cmdmod.cmod_flags & CMOD_NOSWAPFILE)
Bram Moolenaar5803ae62014-03-23 16:04:02 +0100300 buf->b_p_swf = FALSE;
301
Bram Moolenaar4770d092006-01-12 23:22:24 +0000302 /*
303 * When 'updatecount' is non-zero swap file may be opened later.
304 */
305 if (p_uc && buf->b_p_swf)
306 buf->b_may_swap = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000307 else
Bram Moolenaar4770d092006-01-12 23:22:24 +0000308 buf->b_may_swap = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000309
Bram Moolenaar4770d092006-01-12 23:22:24 +0000310 /*
311 * Open the memfile. No swap file is created yet.
312 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000313 mfp = mf_open(NULL, 0);
314 if (mfp == NULL)
315 goto error;
316
Bram Moolenaar4770d092006-01-12 23:22:24 +0000317 buf->b_ml.ml_mfp = mfp;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200318#ifdef FEAT_CRYPT
319 mfp->mf_buffer = buf;
320#endif
Bram Moolenaar4770d092006-01-12 23:22:24 +0000321 buf->b_ml.ml_flags = ML_EMPTY;
322 buf->b_ml.ml_line_count = 1;
Bram Moolenaar592e0a22004-07-03 16:05:59 +0000323#ifdef FEAT_LINEBREAK
324 curwin->w_nrwidth_line_count = 0;
325#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000326
Bram Moolenaar071d4272004-06-13 20:20:40 +0000327/*
328 * fill block0 struct and write page 0
329 */
330 if ((hp = mf_new(mfp, FALSE, 1)) == NULL)
331 goto error;
332 if (hp->bh_bnum != 0)
333 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100334 iemsg(_("E298: Didn't get block nr 0?"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000335 goto error;
336 }
337 b0p = (ZERO_BL *)(hp->bh_data);
338
339 b0p->b0_id[0] = BLOCK0_ID0;
340 b0p->b0_id[1] = BLOCK0_ID1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000341 b0p->b0_magic_long = (long)B0_MAGIC_LONG;
342 b0p->b0_magic_int = (int)B0_MAGIC_INT;
343 b0p->b0_magic_short = (short)B0_MAGIC_SHORT;
344 b0p->b0_magic_char = B0_MAGIC_CHAR;
Bram Moolenaar22c10562018-05-26 17:35:27 +0200345 mch_memmove(b0p->b0_version, "VIM ", 4);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000346 STRNCPY(b0p->b0_version + 4, Version, 6);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000347 long_to_char((long)mfp->mf_page_size, b0p->b0_page_size);
Bram Moolenaar4770d092006-01-12 23:22:24 +0000348
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000349#ifdef FEAT_SPELL
350 if (!buf->b_spell)
351#endif
Bram Moolenaar4770d092006-01-12 23:22:24 +0000352 {
353 b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0;
354 b0p->b0_flags = get_fileformat(buf) + 1;
355 set_b0_fname(b0p, buf);
356 (void)get_user_name(b0p->b0_uname, B0_UNAME_SIZE);
357 b0p->b0_uname[B0_UNAME_SIZE - 1] = NUL;
358 mch_get_host_name(b0p->b0_hname, B0_HNAME_SIZE);
359 b0p->b0_hname[B0_HNAME_SIZE - 1] = NUL;
360 long_to_char(mch_get_pid(), b0p->b0_pid);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200361#ifdef FEAT_CRYPT
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200362 ml_set_b0_crypt(buf, b0p);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200363#endif
Bram Moolenaar4770d092006-01-12 23:22:24 +0000364 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000365
366 /*
367 * Always sync block number 0 to disk, so we can check the file name in
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200368 * the swap file in findswapname(). Don't do this for a help files or
369 * a spell buffer though.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000370 * Only works when there's a swapfile, otherwise it's done when the file
371 * is created.
372 */
373 mf_put(mfp, hp, TRUE, FALSE);
Bram Moolenaar4770d092006-01-12 23:22:24 +0000374 if (!buf->b_help && !B_SPELL(buf))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000375 (void)mf_sync(mfp, 0);
376
Bram Moolenaar4770d092006-01-12 23:22:24 +0000377 /*
378 * Fill in root pointer block and write page 1.
379 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000380 if ((hp = ml_new_ptr(mfp)) == NULL)
381 goto error;
382 if (hp->bh_bnum != 1)
383 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100384 iemsg(_("E298: Didn't get block nr 1?"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000385 goto error;
386 }
387 pp = (PTR_BL *)(hp->bh_data);
388 pp->pb_count = 1;
389 pp->pb_pointer[0].pe_bnum = 2;
390 pp->pb_pointer[0].pe_page_count = 1;
391 pp->pb_pointer[0].pe_old_lnum = 1;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100392 pp->pb_pointer[0].pe_line_count = 1; // line count after insertion
Bram Moolenaar071d4272004-06-13 20:20:40 +0000393 mf_put(mfp, hp, TRUE, FALSE);
394
Bram Moolenaar4770d092006-01-12 23:22:24 +0000395 /*
396 * Allocate first data block and create an empty line 1.
397 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000398 if ((hp = ml_new_data(mfp, FALSE, 1)) == NULL)
399 goto error;
400 if (hp->bh_bnum != 2)
401 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100402 iemsg(_("E298: Didn't get block nr 2?"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000403 goto error;
404 }
405
406 dp = (DATA_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100407 dp->db_index[0] = --dp->db_txt_start; // at end of block
Bram Moolenaar071d4272004-06-13 20:20:40 +0000408 dp->db_free -= 1 + INDEX_SIZE;
409 dp->db_line_count = 1;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100410 *((char_u *)dp + dp->db_txt_start) = NUL; // empty line
Bram Moolenaar071d4272004-06-13 20:20:40 +0000411
412 return OK;
413
414error:
415 if (mfp != NULL)
416 {
417 if (hp)
418 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100419 mf_close(mfp, TRUE); // will also free(mfp->mf_fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000420 }
Bram Moolenaar4770d092006-01-12 23:22:24 +0000421 buf->b_ml.ml_mfp = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000422 return FAIL;
423}
424
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200425#if defined(FEAT_CRYPT) || defined(PROTO)
426/*
Bram Moolenaar2be79502014-08-13 21:58:28 +0200427 * Prepare encryption for "buf" for the current key and method.
428 */
429 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100430ml_set_mfp_crypt(buf_T *buf)
Bram Moolenaar2be79502014-08-13 21:58:28 +0200431{
432 if (*buf->b_p_key != NUL)
433 {
434 int method_nr = crypt_get_method_nr(buf);
435
Christian Brabandtf573c6e2021-06-20 14:02:16 +0200436 if (method_nr > CRYPT_M_ZIP && method_nr < CRYPT_M_SOD)
Bram Moolenaar2be79502014-08-13 21:58:28 +0200437 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100438 // Generate a seed and store it in the memfile.
Bram Moolenaar2be79502014-08-13 21:58:28 +0200439 sha2_seed(buf->b_ml.ml_mfp->mf_seed, MF_SEED_LEN, NULL, 0);
440 }
Christian Brabandtf573c6e2021-06-20 14:02:16 +0200441#ifdef FEAT_SODIUM
442 else if (method_nr == CRYPT_M_SOD)
443 randombytes_buf(buf->b_ml.ml_mfp->mf_seed, MF_SEED_LEN);
444 #endif
Bram Moolenaar2be79502014-08-13 21:58:28 +0200445 }
446}
447
448/*
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200449 * Prepare encryption for "buf" with block 0 "b0p".
450 */
451 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100452ml_set_b0_crypt(buf_T *buf, ZERO_BL *b0p)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200453{
454 if (*buf->b_p_key == NUL)
455 b0p->b0_id[1] = BLOCK0_ID1;
456 else
457 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200458 int method_nr = crypt_get_method_nr(buf);
459
460 b0p->b0_id[1] = id1_codes[method_nr];
Christian Brabandtf573c6e2021-06-20 14:02:16 +0200461 if (method_nr > CRYPT_M_ZIP && method_nr < CRYPT_M_SOD)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200462 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100463 // Generate a seed and store it in block 0 and in the memfile.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200464 sha2_seed(&b0p->b0_seed, MF_SEED_LEN, NULL, 0);
465 mch_memmove(buf->b_ml.ml_mfp->mf_seed, &b0p->b0_seed, MF_SEED_LEN);
466 }
467 }
468}
469
470/*
471 * Called after the crypt key or 'cryptmethod' was changed for "buf".
472 * Will apply this to the swapfile.
473 * "old_key" is the previous key. It is equal to buf->b_p_key when
474 * 'cryptmethod' is changed.
Bram Moolenaar49771f42010-07-20 17:32:38 +0200475 * "old_cm" is the previous 'cryptmethod'. It is equal to the current
476 * 'cryptmethod' when 'key' is changed.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200477 */
478 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100479ml_set_crypt_key(
480 buf_T *buf,
481 char_u *old_key,
482 char_u *old_cm)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200483{
484 memfile_T *mfp = buf->b_ml.ml_mfp;
485 bhdr_T *hp;
486 int page_count;
487 int idx;
488 long error;
489 infoptr_T *ip;
490 PTR_BL *pp;
491 DATA_BL *dp;
492 blocknr_T bnum;
493 int top;
Bram Moolenaarbc563362015-06-09 18:35:25 +0200494 int old_method;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200495
Christian Brabandtf573c6e2021-06-20 14:02:16 +0200496 if (mfp == NULL || mfp->mf_fd < 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100497 return; // no memfile yet, nothing to do
Bram Moolenaarbc563362015-06-09 18:35:25 +0200498 old_method = crypt_method_nr_from_name(old_cm);
499
Christian Brabandt226b28b2021-06-21 21:08:08 +0200500 // Swapfile encryption not supported by XChaCha20
501 if (crypt_get_method_nr(buf) == CRYPT_M_SOD && *buf->b_p_key != NUL)
Christian Brabandtf573c6e2021-06-20 14:02:16 +0200502 {
503 // close the swapfile
504 mf_close_file(buf, TRUE);
505 buf->b_p_swf = FALSE;
506 return;
507 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100508 // First make sure the swapfile is in a consistent state, using the old
509 // key and method.
Bram Moolenaarbc563362015-06-09 18:35:25 +0200510 {
511 char_u *new_key = buf->b_p_key;
512 char_u *new_buf_cm = buf->b_p_cm;
513
514 buf->b_p_key = old_key;
515 buf->b_p_cm = old_cm;
516 ml_preserve(buf, FALSE);
517 buf->b_p_key = new_key;
518 buf->b_p_cm = new_buf_cm;
519 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200520
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100521 // Set the key, method and seed to be used for reading, these must be the
522 // old values.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200523 mfp->mf_old_key = old_key;
Bram Moolenaarbc563362015-06-09 18:35:25 +0200524 mfp->mf_old_cm = old_method;
525 if (old_method > 0 && *old_key != NUL)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200526 mch_memmove(mfp->mf_old_seed, mfp->mf_seed, MF_SEED_LEN);
527
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100528 // Update block 0 with the crypt flag and may set a new seed.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200529 ml_upd_block0(buf, UB_CRYPT);
530
531 if (mfp->mf_infile_count > 2)
532 {
533 /*
534 * Need to read back all data blocks from disk, decrypt them with the
535 * old key/method and mark them to be written. The algorithm is
536 * similar to what happens in ml_recover(), but we skip negative block
537 * numbers.
538 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100539 ml_flush_line(buf); // flush buffered line
540 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush locked block
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200541
542 hp = NULL;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100543 bnum = 1; // start with block 1
544 page_count = 1; // which is 1 page
545 idx = 0; // start with first index in block 1
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200546 error = 0;
547 buf->b_ml.ml_stack_top = 0;
Bram Moolenaard23a8232018-02-10 18:45:26 +0100548 VIM_CLEAR(buf->b_ml.ml_stack);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100549 buf->b_ml.ml_stack_size = 0; // no stack yet
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200550
551 for ( ; !got_int; line_breakcheck())
552 {
553 if (hp != NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100554 mf_put(mfp, hp, FALSE, FALSE); // release previous block
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200555
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100556 // get the block (pointer or data)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200557 if ((hp = mf_get(mfp, (blocknr_T)bnum, page_count)) == NULL)
558 {
559 if (bnum == 1)
560 break;
561 ++error;
562 }
563 else
564 {
565 pp = (PTR_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100566 if (pp->pb_id == PTR_ID) // it is a pointer block
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200567 {
568 if (pp->pb_count == 0)
569 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100570 // empty block?
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200571 ++error;
572 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100573 else if (idx < (int)pp->pb_count) // go a block deeper
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200574 {
575 if (pp->pb_pointer[idx].pe_bnum < 0)
576 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100577 // Skip data block with negative block number.
578 // Should not happen, because of the ml_preserve()
579 // above. Get same block again for next index.
Bram Moolenaar4b7214e2019-01-03 21:55:32 +0100580 ++idx;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200581 continue;
582 }
583
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100584 // going one block deeper in the tree, new entry in
585 // stack
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200586 if ((top = ml_add_stack(buf)) < 0)
587 {
588 ++error;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100589 break; // out of memory
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200590 }
591 ip = &(buf->b_ml.ml_stack[top]);
592 ip->ip_bnum = bnum;
593 ip->ip_index = idx;
594
595 bnum = pp->pb_pointer[idx].pe_bnum;
596 page_count = pp->pb_pointer[idx].pe_page_count;
Bram Moolenaarbc563362015-06-09 18:35:25 +0200597 idx = 0;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200598 continue;
599 }
600 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100601 else // not a pointer block
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200602 {
603 dp = (DATA_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100604 if (dp->db_id != DATA_ID) // block id wrong
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200605 ++error;
606 else
607 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100608 // It is a data block, need to write it back to disk.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200609 mf_put(mfp, hp, TRUE, FALSE);
610 hp = NULL;
611 }
612 }
613 }
614
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100615 if (buf->b_ml.ml_stack_top == 0) // finished
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200616 break;
617
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100618 // go one block up in the tree
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200619 ip = &(buf->b_ml.ml_stack[--(buf->b_ml.ml_stack_top)]);
620 bnum = ip->ip_bnum;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100621 idx = ip->ip_index + 1; // go to next index
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200622 page_count = 1;
623 }
Bram Moolenaarbc563362015-06-09 18:35:25 +0200624 if (hp != NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100625 mf_put(mfp, hp, FALSE, FALSE); // release previous block
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +0100626
627 if (error > 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100628 emsg(_("E843: Error while updating swap file crypt"));
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200629 }
630
631 mfp->mf_old_key = NULL;
632}
633#endif
634
Bram Moolenaar071d4272004-06-13 20:20:40 +0000635/*
636 * ml_setname() is called when the file name of "buf" has been changed.
637 * It may rename the swap file.
638 */
639 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100640ml_setname(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000641{
642 int success = FALSE;
643 memfile_T *mfp;
644 char_u *fname;
645 char_u *dirp;
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100646#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000647 char_u *p;
648#endif
649
650 mfp = buf->b_ml.ml_mfp;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100651 if (mfp->mf_fd < 0) // there is no swap file yet
Bram Moolenaar071d4272004-06-13 20:20:40 +0000652 {
653 /*
654 * When 'updatecount' is 0 and 'noswapfile' there is no swap file.
655 * For help files we will make a swap file now.
656 */
Bram Moolenaare1004402020-10-24 20:49:43 +0200657 if (p_uc != 0 && (cmdmod.cmod_flags & CMOD_NOSWAPFILE) == 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100658 ml_open_file(buf); // create a swap file
Bram Moolenaar071d4272004-06-13 20:20:40 +0000659 return;
660 }
661
662 /*
663 * Try all directories in the 'directory' option.
664 */
665 dirp = p_dir;
666 for (;;)
667 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100668 if (*dirp == NUL) // tried all directories, fail
Bram Moolenaar071d4272004-06-13 20:20:40 +0000669 break;
Bram Moolenaar8fc061c2004-12-29 21:03:02 +0000670 fname = findswapname(buf, &dirp, mfp->mf_fname);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100671 // alloc's fname
672 if (dirp == NULL) // out of memory
Bram Moolenaarf541c362011-10-26 11:44:18 +0200673 break;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100674 if (fname == NULL) // no file name found for this dir
Bram Moolenaar071d4272004-06-13 20:20:40 +0000675 continue;
676
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100677#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000678 /*
679 * Set full pathname for swap file now, because a ":!cd dir" may
680 * change directory without us knowing it.
681 */
682 p = FullName_save(fname, FALSE);
683 vim_free(fname);
684 fname = p;
685 if (fname == NULL)
686 continue;
687#endif
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100688 // if the file name is the same we don't have to do anything
Bram Moolenaar071d4272004-06-13 20:20:40 +0000689 if (fnamecmp(fname, mfp->mf_fname) == 0)
690 {
691 vim_free(fname);
692 success = TRUE;
693 break;
694 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100695 // need to close the swap file before renaming
Bram Moolenaar071d4272004-06-13 20:20:40 +0000696 if (mfp->mf_fd >= 0)
697 {
698 close(mfp->mf_fd);
699 mfp->mf_fd = -1;
700 }
701
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100702 // try to rename the swap file
Bram Moolenaar071d4272004-06-13 20:20:40 +0000703 if (vim_rename(mfp->mf_fname, fname) == 0)
704 {
705 success = TRUE;
706 vim_free(mfp->mf_fname);
707 mfp->mf_fname = fname;
708 vim_free(mfp->mf_ffname);
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100709#if defined(MSWIN)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100710 mfp->mf_ffname = NULL; // mf_fname is full pathname already
Bram Moolenaar071d4272004-06-13 20:20:40 +0000711#else
712 mf_set_ffname(mfp);
713#endif
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200714 ml_upd_block0(buf, UB_SAME_DIR);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000715 break;
716 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100717 vim_free(fname); // this fname didn't work, try another
Bram Moolenaar071d4272004-06-13 20:20:40 +0000718 }
719
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100720 if (mfp->mf_fd == -1) // need to (re)open the swap file
Bram Moolenaar071d4272004-06-13 20:20:40 +0000721 {
722 mfp->mf_fd = mch_open((char *)mfp->mf_fname, O_RDWR | O_EXTRA, 0);
723 if (mfp->mf_fd < 0)
724 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100725 // could not (re)open the swap file, what can we do????
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100726 emsg(_("E301: Oops, lost the swap file!!!"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000727 return;
728 }
Bram Moolenaarf05da212009-11-17 16:13:15 +0000729#ifdef HAVE_FD_CLOEXEC
730 {
731 int fdflags = fcntl(mfp->mf_fd, F_GETFD);
732 if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0)
Bram Moolenaarfbc4b4d2016-02-07 15:14:01 +0100733 (void)fcntl(mfp->mf_fd, F_SETFD, fdflags | FD_CLOEXEC);
Bram Moolenaarf05da212009-11-17 16:13:15 +0000734 }
735#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000736 }
737 if (!success)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100738 emsg(_("E302: Could not rename swap file"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000739}
740
741/*
742 * Open a file for the memfile for all buffers that are not readonly or have
743 * been modified.
744 * Used when 'updatecount' changes from zero to non-zero.
745 */
746 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100747ml_open_files(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000748{
749 buf_T *buf;
750
Bram Moolenaar29323592016-07-24 22:04:11 +0200751 FOR_ALL_BUFFERS(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000752 if (!buf->b_p_ro || buf->b_changed)
753 ml_open_file(buf);
754}
755
756/*
757 * Open a swap file for an existing memfile, if there is no swap file yet.
758 * If we are unable to find a file name, mf_fname will be NULL
759 * and the memfile will be in memory only (no recovery possible).
760 */
761 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100762ml_open_file(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000763{
764 memfile_T *mfp;
765 char_u *fname;
766 char_u *dirp;
767
768 mfp = buf->b_ml.ml_mfp;
Bram Moolenaare1004402020-10-24 20:49:43 +0200769 if (mfp == NULL || mfp->mf_fd >= 0 || !buf->b_p_swf
770 || (cmdmod.cmod_flags & CMOD_NOSWAPFILE))
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100771 return; // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +0000772
Bram Moolenaara1956f62006-03-12 22:18:00 +0000773#ifdef FEAT_SPELL
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100774 // For a spell buffer use a temp file name.
Bram Moolenaar4770d092006-01-12 23:22:24 +0000775 if (buf->b_spell)
776 {
Bram Moolenaare5c421c2015-03-31 13:33:08 +0200777 fname = vim_tempname('s', FALSE);
Bram Moolenaar4770d092006-01-12 23:22:24 +0000778 if (fname != NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100779 (void)mf_open_file(mfp, fname); // consumes fname!
Bram Moolenaar4770d092006-01-12 23:22:24 +0000780 buf->b_may_swap = FALSE;
781 return;
782 }
783#endif
784
Bram Moolenaar071d4272004-06-13 20:20:40 +0000785 /*
786 * Try all directories in 'directory' option.
787 */
788 dirp = p_dir;
789 for (;;)
790 {
791 if (*dirp == NUL)
792 break;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100793 // There is a small chance that between choosing the swap file name
794 // and creating it, another Vim creates the file. In that case the
795 // creation will fail and we will use another directory.
796 fname = findswapname(buf, &dirp, NULL); // allocates fname
Bram Moolenaarf541c362011-10-26 11:44:18 +0200797 if (dirp == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100798 break; // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +0000799 if (fname == NULL)
800 continue;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100801 if (mf_open_file(mfp, fname) == OK) // consumes fname!
Bram Moolenaar071d4272004-06-13 20:20:40 +0000802 {
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100803#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000804 /*
805 * set full pathname for swap file now, because a ":!cd dir" may
806 * change directory without us knowing it.
807 */
808 mf_fullname(mfp);
809#endif
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200810 ml_upd_block0(buf, UB_SAME_DIR);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000811
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100812 // Flush block zero, so others can read it
Bram Moolenaar071d4272004-06-13 20:20:40 +0000813 if (mf_sync(mfp, MFS_ZERO) == OK)
Bram Moolenaarc32840f2006-01-14 21:23:38 +0000814 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100815 // Mark all blocks that should be in the swapfile as dirty.
816 // Needed for when the 'swapfile' option was reset, so that
817 // the swap file was deleted, and then on again.
Bram Moolenaarc32840f2006-01-14 21:23:38 +0000818 mf_set_dirty(mfp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000819 break;
Bram Moolenaarc32840f2006-01-14 21:23:38 +0000820 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100821 // Writing block 0 failed: close the file and try another dir
Bram Moolenaar071d4272004-06-13 20:20:40 +0000822 mf_close_file(buf, FALSE);
823 }
824 }
825
Bram Moolenaar00e192b2019-10-19 17:01:28 +0200826 if (*p_dir != NUL && mfp->mf_fname == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000827 {
Bram Moolenaar00e192b2019-10-19 17:01:28 +0200828 need_wait_return = TRUE; // call wait_return later
Bram Moolenaar071d4272004-06-13 20:20:40 +0000829 ++no_wait_return;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100830 (void)semsg(_("E303: Unable to open swap file for \"%s\", recovery impossible"),
Bram Moolenaare1704ba2012-10-03 18:25:00 +0200831 buf_spname(buf) != NULL ? buf_spname(buf) : buf->b_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000832 --no_wait_return;
833 }
834
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100835 // don't try to open a swap file again
Bram Moolenaar071d4272004-06-13 20:20:40 +0000836 buf->b_may_swap = FALSE;
837}
838
839/*
840 * If still need to create a swap file, and starting to edit a not-readonly
841 * file, or reading into an existing buffer, create a swap file now.
842 */
843 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100844check_need_swap(
Bram Moolenaar2f0f8712018-08-21 18:50:18 +0200845 int newfile) // reading file into new buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +0000846{
Bram Moolenaar2f0f8712018-08-21 18:50:18 +0200847 int old_msg_silent = msg_silent; // might be reset by an E325 message
848
Bram Moolenaar071d4272004-06-13 20:20:40 +0000849 if (curbuf->b_may_swap && (!curbuf->b_p_ro || !newfile))
850 ml_open_file(curbuf);
Bram Moolenaar2f0f8712018-08-21 18:50:18 +0200851 msg_silent = old_msg_silent;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000852}
853
854/*
855 * Close memline for buffer 'buf'.
856 * If 'del_file' is TRUE, delete the swap file
857 */
858 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100859ml_close(buf_T *buf, int del_file)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000860{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100861 if (buf->b_ml.ml_mfp == NULL) // not open
Bram Moolenaar071d4272004-06-13 20:20:40 +0000862 return;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100863 mf_close(buf->b_ml.ml_mfp, del_file); // close the .swp file
Bram Moolenaar071d4272004-06-13 20:20:40 +0000864 if (buf->b_ml.ml_line_lnum != 0 && (buf->b_ml.ml_flags & ML_LINE_DIRTY))
865 vim_free(buf->b_ml.ml_line_ptr);
866 vim_free(buf->b_ml.ml_stack);
867#ifdef FEAT_BYTEOFF
Bram Moolenaard23a8232018-02-10 18:45:26 +0100868 VIM_CLEAR(buf->b_ml.ml_chunksize);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000869#endif
870 buf->b_ml.ml_mfp = NULL;
871
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100872 // Reset the "recovered" flag, give the ATTENTION prompt the next time
873 // this buffer is loaded.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000874 buf->b_flags &= ~BF_RECOVERED;
875}
876
877/*
878 * Close all existing memlines and memfiles.
879 * Only used when exiting.
880 * When 'del_file' is TRUE, delete the memfiles.
Bram Moolenaar81bf7082005-02-12 14:31:42 +0000881 * But don't delete files that were ":preserve"d when we are POSIX compatible.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000882 */
883 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100884ml_close_all(int del_file)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000885{
886 buf_T *buf;
887
Bram Moolenaar29323592016-07-24 22:04:11 +0200888 FOR_ALL_BUFFERS(buf)
Bram Moolenaar81bf7082005-02-12 14:31:42 +0000889 ml_close(buf, del_file && ((buf->b_flags & BF_PRESERVED) == 0
890 || vim_strchr(p_cpo, CPO_PRESERVE) == NULL));
Bram Moolenaar34b466e2013-11-28 17:41:46 +0100891#ifdef FEAT_SPELL
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100892 spell_delete_wordlist(); // delete the internal wordlist
Bram Moolenaar34b466e2013-11-28 17:41:46 +0100893#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000894#ifdef TEMPDIRNAMES
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100895 vim_deltempdir(); // delete created temp directory
Bram Moolenaar071d4272004-06-13 20:20:40 +0000896#endif
897}
898
899/*
900 * Close all memfiles for not modified buffers.
901 * Only use just before exiting!
902 */
903 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100904ml_close_notmod(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000905{
906 buf_T *buf;
907
Bram Moolenaar29323592016-07-24 22:04:11 +0200908 FOR_ALL_BUFFERS(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000909 if (!bufIsChanged(buf))
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100910 ml_close(buf, TRUE); // close all not-modified buffers
Bram Moolenaar071d4272004-06-13 20:20:40 +0000911}
912
913/*
914 * Update the timestamp in the .swp file.
915 * Used when the file has been written.
916 */
917 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100918ml_timestamp(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000919{
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200920 ml_upd_block0(buf, UB_FNAME);
921}
922
923/*
924 * Return FAIL when the ID of "b0p" is wrong.
925 */
926 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100927ml_check_b0_id(ZERO_BL *b0p)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200928{
929 if (b0p->b0_id[0] != BLOCK0_ID0
930 || (b0p->b0_id[1] != BLOCK0_ID1
931 && b0p->b0_id[1] != BLOCK0_ID1_C0
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200932 && b0p->b0_id[1] != BLOCK0_ID1_C1
Christian Brabandtf573c6e2021-06-20 14:02:16 +0200933 && b0p->b0_id[1] != BLOCK0_ID1_C2
934 && b0p->b0_id[1] != BLOCK0_ID1_C3)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200935 )
936 return FAIL;
937 return OK;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000938}
939
940/*
941 * Update the timestamp or the B0_SAME_DIR flag of the .swp file.
942 */
943 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100944ml_upd_block0(buf_T *buf, upd_block0_T what)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000945{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000946 memfile_T *mfp;
947 bhdr_T *hp;
948 ZERO_BL *b0p;
949
950 mfp = buf->b_ml.ml_mfp;
Bram Moolenaar2be79502014-08-13 21:58:28 +0200951 if (mfp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000952 return;
Bram Moolenaar2be79502014-08-13 21:58:28 +0200953 hp = mf_get(mfp, (blocknr_T)0, 1);
954 if (hp == NULL)
955 {
956#ifdef FEAT_CRYPT
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100957 // Possibly update the seed in the memfile before there is a block0.
Bram Moolenaar2be79502014-08-13 21:58:28 +0200958 if (what == UB_CRYPT)
959 ml_set_mfp_crypt(buf);
960#endif
961 return;
962 }
963
Bram Moolenaar071d4272004-06-13 20:20:40 +0000964 b0p = (ZERO_BL *)(hp->bh_data);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200965 if (ml_check_b0_id(b0p) == FAIL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100966 iemsg(_("E304: ml_upd_block0(): Didn't get block 0??"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000967 else
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000968 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200969 if (what == UB_FNAME)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000970 set_b0_fname(b0p, buf);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200971#ifdef FEAT_CRYPT
972 else if (what == UB_CRYPT)
973 ml_set_b0_crypt(buf, b0p);
974#endif
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100975 else // what == UB_SAME_DIR
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000976 set_b0_dir_flag(b0p, buf);
977 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000978 mf_put(mfp, hp, TRUE, FALSE);
979}
980
981/*
982 * Write file name and timestamp into block 0 of a swap file.
983 * Also set buf->b_mtime.
984 * Don't use NameBuff[]!!!
985 */
986 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100987set_b0_fname(ZERO_BL *b0p, buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000988{
Bram Moolenaar8767f522016-07-01 17:17:39 +0200989 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000990
991 if (buf->b_ffname == NULL)
992 b0p->b0_fname[0] = NUL;
993 else
994 {
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100995#if defined(MSWIN) || defined(AMIGA)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100996 // Systems that cannot translate "~user" back into a path: copy the
997 // file name unmodified. Do use slashes instead of backslashes for
998 // portability.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200999 vim_strncpy(b0p->b0_fname, buf->b_ffname, B0_FNAME_SIZE_CRYPT - 1);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001000# ifdef BACKSLASH_IN_FILENAME
1001 forward_slash(b0p->b0_fname);
1002# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001003#else
1004 size_t flen, ulen;
1005 char_u uname[B0_UNAME_SIZE];
1006
1007 /*
1008 * For a file under the home directory of the current user, we try to
1009 * replace the home directory path with "~user". This helps when
1010 * editing the same file on different machines over a network.
1011 * First replace home dir path with "~/" with home_replace().
1012 * Then insert the user name to get "~user/".
1013 */
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001014 home_replace(NULL, buf->b_ffname, b0p->b0_fname,
1015 B0_FNAME_SIZE_CRYPT, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001016 if (b0p->b0_fname[0] == '~')
1017 {
1018 flen = STRLEN(b0p->b0_fname);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001019 // If there is no user name or it is too long, don't use "~/"
Bram Moolenaar071d4272004-06-13 20:20:40 +00001020 if (get_user_name(uname, B0_UNAME_SIZE) == FAIL
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001021 || (ulen = STRLEN(uname)) + flen > B0_FNAME_SIZE_CRYPT - 1)
1022 vim_strncpy(b0p->b0_fname, buf->b_ffname,
1023 B0_FNAME_SIZE_CRYPT - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001024 else
1025 {
1026 mch_memmove(b0p->b0_fname + ulen + 1, b0p->b0_fname + 1, flen);
1027 mch_memmove(b0p->b0_fname + 1, uname, ulen);
1028 }
1029 }
1030#endif
1031 if (mch_stat((char *)buf->b_ffname, &st) >= 0)
1032 {
1033 long_to_char((long)st.st_mtime, b0p->b0_mtime);
1034#ifdef CHECK_INODE
1035 long_to_char((long)st.st_ino, b0p->b0_ino);
1036#endif
1037 buf_store_time(buf, &st, buf->b_ffname);
1038 buf->b_mtime_read = buf->b_mtime;
1039 }
1040 else
1041 {
1042 long_to_char(0L, b0p->b0_mtime);
1043#ifdef CHECK_INODE
1044 long_to_char(0L, b0p->b0_ino);
1045#endif
1046 buf->b_mtime = 0;
1047 buf->b_mtime_read = 0;
1048 buf->b_orig_size = 0;
1049 buf->b_orig_mode = 0;
1050 }
1051 }
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001052
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001053 // Also add the 'fileencoding' if there is room.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001054 add_b0_fenc(b0p, curbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001055}
1056
1057/*
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001058 * Update the B0_SAME_DIR flag of the swap file. It's set if the file and the
1059 * swapfile for "buf" are in the same directory.
1060 * This is fail safe: if we are not sure the directories are equal the flag is
1061 * not set.
1062 */
1063 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001064set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001065{
1066 if (same_directory(buf->b_ml.ml_mfp->mf_fname, buf->b_ffname))
1067 b0p->b0_flags |= B0_SAME_DIR;
1068 else
1069 b0p->b0_flags &= ~B0_SAME_DIR;
1070}
1071
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001072/*
1073 * When there is room, add the 'fileencoding' to block zero.
1074 */
1075 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001076add_b0_fenc(
1077 ZERO_BL *b0p,
1078 buf_T *buf)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001079{
1080 int n;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001081 int size = B0_FNAME_SIZE_NOCRYPT;
1082
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01001083#ifdef FEAT_CRYPT
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001084 // Without encryption use the same offset as in Vim 7.2 to be compatible.
1085 // With encryption it's OK to move elsewhere, the swap file is not
1086 // compatible anyway.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001087 if (*buf->b_p_key != NUL)
1088 size = B0_FNAME_SIZE_CRYPT;
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01001089#endif
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001090
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00001091 n = (int)STRLEN(buf->b_p_fenc);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001092 if ((int)STRLEN(b0p->b0_fname) + n + 1 > size)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001093 b0p->b0_flags &= ~B0_HAS_FENC;
1094 else
1095 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001096 mch_memmove((char *)b0p->b0_fname + size - n,
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001097 (char *)buf->b_p_fenc, (size_t)n);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001098 *(b0p->b0_fname + size - n - 1) = NUL;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001099 b0p->b0_flags |= B0_HAS_FENC;
1100 }
1101}
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001102
Bram Moolenaarf52f0602021-03-10 21:26:37 +01001103#if defined(HAVE_SYS_SYSINFO_H) && defined(HAVE_SYSINFO_UPTIME)
1104# include <sys/sysinfo.h>
1105#endif
1106
=?UTF-8?q?Ola=20S=C3=B6der?=599a6e52021-07-06 20:15:46 +02001107#if defined(UNIX) || defined(MSWIN)
Bram Moolenaarf52f0602021-03-10 21:26:37 +01001108/*
1109 * Return TRUE if the process with number "b0p->b0_pid" is still running.
1110 * "swap_fname" is the name of the swap file, if it's from before a reboot then
1111 * the result is FALSE;
1112 */
1113 static int
1114swapfile_process_running(ZERO_BL *b0p, char_u *swap_fname UNUSED)
1115{
=?UTF-8?q?Ola=20S=C3=B6der?=599a6e52021-07-06 20:15:46 +02001116# ifdef HAVE_SYSINFO_UPTIME
Bram Moolenaarf52f0602021-03-10 21:26:37 +01001117 stat_T st;
1118 struct sysinfo sinfo;
1119
1120 // If the system rebooted after when the swap file was written then the
1121 // process can't be running now.
1122 if (mch_stat((char *)swap_fname, &st) != -1
1123 && sysinfo(&sinfo) == 0
Bram Moolenaar23b32a82021-03-10 21:55:46 +01001124 && st.st_mtime < time(NULL) - (
=?UTF-8?q?Ola=20S=C3=B6der?=599a6e52021-07-06 20:15:46 +02001125# ifdef FEAT_EVAL
Bram Moolenaar23b32a82021-03-10 21:55:46 +01001126 override_sysinfo_uptime >= 0 ? override_sysinfo_uptime :
=?UTF-8?q?Ola=20S=C3=B6der?=599a6e52021-07-06 20:15:46 +02001127# endif
Bram Moolenaar23b32a82021-03-10 21:55:46 +01001128 sinfo.uptime))
Bram Moolenaarf52f0602021-03-10 21:26:37 +01001129 return FALSE;
=?UTF-8?q?Ola=20S=C3=B6der?=599a6e52021-07-06 20:15:46 +02001130# endif
Bram Moolenaarf52f0602021-03-10 21:26:37 +01001131 return mch_process_running(char_to_long(b0p->b0_pid));
1132}
=?UTF-8?q?Ola=20S=C3=B6der?=599a6e52021-07-06 20:15:46 +02001133#endif
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001134
1135/*
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001136 * Try to recover curbuf from the .swp file.
Bram Moolenaar99499b12019-05-23 21:35:48 +02001137 * If "checkext" is TRUE, check the extension and detect whether it is
1138 * a swap file.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001139 */
1140 void
Bram Moolenaar99499b12019-05-23 21:35:48 +02001141ml_recover(int checkext)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001142{
1143 buf_T *buf = NULL;
1144 memfile_T *mfp = NULL;
1145 char_u *fname;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001146 char_u *fname_used = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001147 bhdr_T *hp = NULL;
1148 ZERO_BL *b0p;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001149 int b0_ff;
1150 char_u *b0_fenc = NULL;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001151#ifdef FEAT_CRYPT
1152 int b0_cm = -1;
1153#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001154 PTR_BL *pp;
1155 DATA_BL *dp;
1156 infoptr_T *ip;
1157 blocknr_T bnum;
1158 int page_count;
Bram Moolenaar8767f522016-07-01 17:17:39 +02001159 stat_T org_stat, swp_stat;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001160 int len;
1161 int directly;
1162 linenr_T lnum;
1163 char_u *p;
1164 int i;
1165 long error;
1166 int cannot_open;
1167 linenr_T line_count;
1168 int has_error;
1169 int idx;
1170 int top;
1171 int txt_start;
Bram Moolenaar8767f522016-07-01 17:17:39 +02001172 off_T size;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001173 int called_from_main;
1174 int serious_error = TRUE;
1175 long mtime;
1176 int attr;
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001177 int orig_file_status = NOTDONE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001178
1179 recoverymode = TRUE;
1180 called_from_main = (curbuf->b_ml.ml_mfp == NULL);
Bram Moolenaar8820b482017-03-16 17:23:31 +01001181 attr = HL_ATTR(HLF_E);
Bram Moolenaard0ba34a2009-11-03 12:06:23 +00001182
1183 /*
Bram Moolenaaraf903e52017-12-02 15:11:22 +01001184 * If the file name ends in ".s[a-w][a-z]" we assume this is the swap file.
Bram Moolenaard0ba34a2009-11-03 12:06:23 +00001185 * Otherwise a search is done to find the swap file(s).
1186 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001187 fname = curbuf->b_fname;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001188 if (fname == NULL) // When there is no file name
Bram Moolenaar071d4272004-06-13 20:20:40 +00001189 fname = (char_u *)"";
1190 len = (int)STRLEN(fname);
Bram Moolenaar99499b12019-05-23 21:35:48 +02001191 if (checkext && len >= 4 &&
Bram Moolenaare60acc12011-05-10 16:41:25 +02001192#if defined(VMS)
Bram Moolenaar79518e22017-02-17 16:31:35 +01001193 STRNICMP(fname + len - 4, "_s", 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001194#else
Bram Moolenaar79518e22017-02-17 16:31:35 +01001195 STRNICMP(fname + len - 4, ".s", 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001196#endif
Bram Moolenaar79518e22017-02-17 16:31:35 +01001197 == 0
Bram Moolenaaraf903e52017-12-02 15:11:22 +01001198 && vim_strchr((char_u *)"abcdefghijklmnopqrstuvw",
1199 TOLOWER_ASC(fname[len - 2])) != NULL
Bram Moolenaard0ba34a2009-11-03 12:06:23 +00001200 && ASCII_ISALPHA(fname[len - 1]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001201 {
1202 directly = TRUE;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001203 fname_used = vim_strsave(fname); // make a copy for mf_open()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001204 }
1205 else
1206 {
1207 directly = FALSE;
1208
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001209 // count the number of matching swap files
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001210 len = recover_names(fname, FALSE, 0, NULL);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001211 if (len == 0) // no swap files found
Bram Moolenaar071d4272004-06-13 20:20:40 +00001212 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001213 semsg(_("E305: No swap file found for %s"), fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001214 goto theend;
1215 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001216 if (len == 1) // one swap file found, use it
Bram Moolenaar071d4272004-06-13 20:20:40 +00001217 i = 1;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001218 else // several swap files found, choose
Bram Moolenaar071d4272004-06-13 20:20:40 +00001219 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001220 // list the names of the swap files
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001221 (void)recover_names(fname, TRUE, 0, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001222 msg_putchar('\n');
Bram Moolenaar32526b32019-01-19 17:43:09 +01001223 msg_puts(_("Enter number of swap file to use (0 to quit): "));
Bram Moolenaar24bbcfe2005-06-28 23:32:02 +00001224 i = get_number(FALSE, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001225 if (i < 1 || i > len)
1226 goto theend;
1227 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001228 // get the swap file name that will be used
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001229 (void)recover_names(fname, FALSE, i, &fname_used);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001230 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001231 if (fname_used == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001232 goto theend; // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00001233
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001234 // When called from main() still need to initialize storage structure
Bram Moolenaar4770d092006-01-12 23:22:24 +00001235 if (called_from_main && ml_open(curbuf) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001236 getout(1);
1237
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001238 /*
1239 * Allocate a buffer structure for the swap file that is used for recovery.
Bram Moolenaar0ad014c2010-07-25 14:00:46 +02001240 * Only the memline and crypt information in it are really used.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001241 */
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001242 buf = ALLOC_ONE(buf_T);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001243 if (buf == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001244 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001245
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001246 /*
1247 * init fields in memline struct
1248 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001249 buf->b_ml.ml_stack_size = 0; // no stack yet
1250 buf->b_ml.ml_stack = NULL; // no stack yet
1251 buf->b_ml.ml_stack_top = 0; // nothing in the stack
1252 buf->b_ml.ml_line_lnum = 0; // no cached line
1253 buf->b_ml.ml_locked = NULL; // no locked block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001254 buf->b_ml.ml_flags = 0;
Bram Moolenaar0fe849a2010-07-25 15:11:11 +02001255#ifdef FEAT_CRYPT
1256 buf->b_p_key = empty_option;
1257 buf->b_p_cm = empty_option;
1258#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001259
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001260 /*
1261 * open the memfile from the old swap file
1262 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001263 p = vim_strsave(fname_used); // save "fname_used" for the message:
1264 // mf_open() will consume "fname_used"!
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001265 mfp = mf_open(fname_used, O_RDONLY);
1266 fname_used = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001267 if (mfp == NULL || mfp->mf_fd < 0)
1268 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001269 if (fname_used != NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001270 semsg(_("E306: Cannot open %s"), fname_used);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001271 goto theend;
1272 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001273 buf->b_ml.ml_mfp = mfp;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001274#ifdef FEAT_CRYPT
1275 mfp->mf_buffer = buf;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001276#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001277
1278 /*
1279 * The page size set in mf_open() might be different from the page size
1280 * used in the swap file, we must get it from block 0. But to read block
1281 * 0 we need a page size. Use the minimal size for block 0 here, it will
1282 * be set to the real value below.
1283 */
1284 mfp->mf_page_size = MIN_SWAP_PAGE_SIZE;
1285
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001286 /*
1287 * try to read block 0
1288 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001289 if ((hp = mf_get(mfp, (blocknr_T)0, 1)) == NULL)
1290 {
1291 msg_start();
Bram Moolenaar32526b32019-01-19 17:43:09 +01001292 msg_puts_attr(_("Unable to read block 0 from "), attr | MSG_HIST);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001293 msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001294 msg_puts_attr(_("\nMaybe no changes were made or Vim did not update the swap file."),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001295 attr | MSG_HIST);
1296 msg_end();
1297 goto theend;
1298 }
1299 b0p = (ZERO_BL *)(hp->bh_data);
1300 if (STRNCMP(b0p->b0_version, "VIM 3.0", 7) == 0)
1301 {
1302 msg_start();
1303 msg_outtrans_attr(mfp->mf_fname, MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001304 msg_puts_attr(_(" cannot be used with this version of Vim.\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001305 MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001306 msg_puts_attr(_("Use Vim version 3.0.\n"), MSG_HIST);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001307 msg_end();
1308 goto theend;
1309 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001310 if (ml_check_b0_id(b0p) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001311 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001312 semsg(_("E307: %s does not look like a Vim swap file"), mfp->mf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001313 goto theend;
1314 }
1315 if (b0_magic_wrong(b0p))
1316 {
1317 msg_start();
1318 msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
Bram Moolenaar48e330a2016-02-23 14:53:34 +01001319#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001320 if (STRNCMP(b0p->b0_hname, "PC ", 3) == 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001321 msg_puts_attr(_(" cannot be used with this version of Vim.\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001322 attr | MSG_HIST);
1323 else
1324#endif
Bram Moolenaar32526b32019-01-19 17:43:09 +01001325 msg_puts_attr(_(" cannot be used on this computer.\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001326 attr | MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001327 msg_puts_attr(_("The file was created on "), attr | MSG_HIST);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001328 // avoid going past the end of a corrupted hostname
Bram Moolenaar071d4272004-06-13 20:20:40 +00001329 b0p->b0_fname[0] = NUL;
Bram Moolenaar32526b32019-01-19 17:43:09 +01001330 msg_puts_attr((char *)b0p->b0_hname, attr | MSG_HIST);
1331 msg_puts_attr(_(",\nor the file has been damaged."), attr | MSG_HIST);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001332 msg_end();
1333 goto theend;
1334 }
Bram Moolenaar1c536282007-04-26 15:21:56 +00001335
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001336#ifdef FEAT_CRYPT
K.Takataeeec2542021-06-02 13:28:16 +02001337 for (i = 0; i < (int)ARRAY_LENGTH(id1_codes); ++i)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001338 if (id1_codes[i] == b0p->b0_id[1])
1339 b0_cm = i;
1340 if (b0_cm > 0)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001341 mch_memmove(mfp->mf_seed, &b0p->b0_seed, MF_SEED_LEN);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001342 crypt_set_cm_option(buf, b0_cm < 0 ? 0 : b0_cm);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001343#else
1344 if (b0p->b0_id[1] != BLOCK0_ID1)
1345 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001346 semsg(_("E833: %s is encrypted and this version of Vim does not support encryption"), mfp->mf_fname);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001347 goto theend;
1348 }
1349#endif
1350
Bram Moolenaar071d4272004-06-13 20:20:40 +00001351 /*
1352 * If we guessed the wrong page size, we have to recalculate the
1353 * highest block number in the file.
1354 */
1355 if (mfp->mf_page_size != (unsigned)char_to_long(b0p->b0_page_size))
1356 {
Bram Moolenaar1c536282007-04-26 15:21:56 +00001357 unsigned previous_page_size = mfp->mf_page_size;
1358
Bram Moolenaar071d4272004-06-13 20:20:40 +00001359 mf_new_page_size(mfp, (unsigned)char_to_long(b0p->b0_page_size));
Bram Moolenaar1c536282007-04-26 15:21:56 +00001360 if (mfp->mf_page_size < previous_page_size)
1361 {
1362 msg_start();
1363 msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001364 msg_puts_attr(_(" has been damaged (page size is smaller than minimum value).\n"),
Bram Moolenaar1c536282007-04-26 15:21:56 +00001365 attr | MSG_HIST);
1366 msg_end();
1367 goto theend;
1368 }
Bram Moolenaar8767f522016-07-01 17:17:39 +02001369 if ((size = vim_lseek(mfp->mf_fd, (off_T)0L, SEEK_END)) <= 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001370 mfp->mf_blocknr_max = 0; // no file or empty file
Bram Moolenaar071d4272004-06-13 20:20:40 +00001371 else
1372 mfp->mf_blocknr_max = (blocknr_T)(size / mfp->mf_page_size);
1373 mfp->mf_infile_count = mfp->mf_blocknr_max;
Bram Moolenaar1c536282007-04-26 15:21:56 +00001374
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001375 // need to reallocate the memory used to store the data
Bram Moolenaar1c536282007-04-26 15:21:56 +00001376 p = alloc(mfp->mf_page_size);
1377 if (p == NULL)
1378 goto theend;
1379 mch_memmove(p, hp->bh_data, previous_page_size);
1380 vim_free(hp->bh_data);
1381 hp->bh_data = p;
1382 b0p = (ZERO_BL *)(hp->bh_data);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001383 }
1384
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001385 /*
1386 * If .swp file name given directly, use name from swap file for buffer.
1387 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001388 if (directly)
1389 {
1390 expand_env(b0p->b0_fname, NameBuff, MAXPATHL);
1391 if (setfname(curbuf, NameBuff, NULL, TRUE) == FAIL)
1392 goto theend;
1393 }
1394
1395 home_replace(NULL, mfp->mf_fname, NameBuff, MAXPATHL, TRUE);
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001396 smsg(_("Using swap file \"%s\""), NameBuff);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001397
1398 if (buf_spname(curbuf) != NULL)
Bram Moolenaare1704ba2012-10-03 18:25:00 +02001399 vim_strncpy(NameBuff, buf_spname(curbuf), MAXPATHL - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001400 else
1401 home_replace(NULL, curbuf->b_ffname, NameBuff, MAXPATHL, TRUE);
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001402 smsg(_("Original file \"%s\""), NameBuff);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001403 msg_putchar('\n');
1404
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001405 /*
1406 * check date of swap file and original file
1407 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001408 mtime = char_to_long(b0p->b0_mtime);
1409 if (curbuf->b_ffname != NULL
1410 && mch_stat((char *)curbuf->b_ffname, &org_stat) != -1
1411 && ((mch_stat((char *)mfp->mf_fname, &swp_stat) != -1
1412 && org_stat.st_mtime > swp_stat.st_mtime)
1413 || org_stat.st_mtime != mtime))
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001414 emsg(_("E308: Warning: Original file may have been changed"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001415 out_flush();
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001416
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001417 // Get the 'fileformat' and 'fileencoding' from block zero.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001418 b0_ff = (b0p->b0_flags & B0_FF_MASK);
1419 if (b0p->b0_flags & B0_HAS_FENC)
1420 {
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001421 int fnsize = B0_FNAME_SIZE_NOCRYPT;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001422
1423#ifdef FEAT_CRYPT
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001424 // Use the same size as in add_b0_fenc().
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001425 if (b0p->b0_id[1] != BLOCK0_ID1)
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001426 fnsize = B0_FNAME_SIZE_CRYPT;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001427#endif
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001428 for (p = b0p->b0_fname + fnsize; p > b0p->b0_fname && p[-1] != NUL; --p)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001429 ;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001430 b0_fenc = vim_strnsave(p, b0p->b0_fname + fnsize - p);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001431 }
1432
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001433 mf_put(mfp, hp, FALSE, FALSE); // release block 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001434 hp = NULL;
1435
1436 /*
1437 * Now that we are sure that the file is going to be recovered, clear the
1438 * contents of the current buffer.
1439 */
1440 while (!(curbuf->b_ml.ml_flags & ML_EMPTY))
Bram Moolenaarca70c072020-05-30 20:30:46 +02001441 ml_delete((linenr_T)1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001442
1443 /*
1444 * Try reading the original file to obtain the values of 'fileformat',
1445 * 'fileencoding', etc. Ignore errors. The text itself is not used.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001446 * When the file is encrypted the user is asked to enter the key.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001447 */
1448 if (curbuf->b_ffname != NULL)
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001449 orig_file_status = readfile(curbuf->b_ffname, NULL, (linenr_T)0,
Bram Moolenaar071d4272004-06-13 20:20:40 +00001450 (linenr_T)0, (linenr_T)MAXLNUM, NULL, READ_NEW);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001451
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001452#ifdef FEAT_CRYPT
1453 if (b0_cm >= 0)
1454 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001455 // Need to ask the user for the crypt key. If this fails we continue
1456 // without a key, will probably get garbage text.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001457 if (*curbuf->b_p_key != NUL)
1458 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001459 smsg(_("Swap file is encrypted: \"%s\""), fname_used);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001460 msg_puts(_("\nIf you entered a new crypt key but did not write the text file,"));
1461 msg_puts(_("\nenter the new crypt key."));
1462 msg_puts(_("\nIf you wrote the text file after changing the crypt key press enter"));
1463 msg_puts(_("\nto use the same key for text file and swap file"));
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001464 }
1465 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001466 smsg(_(need_key_msg), fname_used);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001467 buf->b_p_key = crypt_get_key(FALSE, FALSE);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001468 if (buf->b_p_key == NULL)
1469 buf->b_p_key = curbuf->b_p_key;
1470 else if (*buf->b_p_key == NUL)
1471 {
1472 vim_free(buf->b_p_key);
1473 buf->b_p_key = curbuf->b_p_key;
1474 }
1475 if (buf->b_p_key == NULL)
1476 buf->b_p_key = empty_option;
1477 }
1478#endif
1479
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001480 // Use the 'fileformat' and 'fileencoding' as stored in the swap file.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001481 if (b0_ff != 0)
1482 set_fileformat(b0_ff - 1, OPT_LOCAL);
1483 if (b0_fenc != NULL)
1484 {
1485 set_option_value((char_u *)"fenc", 0L, b0_fenc, OPT_LOCAL);
1486 vim_free(b0_fenc);
1487 }
Bram Moolenaarc024b462019-06-08 18:07:21 +02001488 unchanged(curbuf, TRUE, TRUE);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001489
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001490 bnum = 1; // start with block 1
1491 page_count = 1; // which is 1 page
1492 lnum = 0; // append after line 0 in curbuf
Bram Moolenaar071d4272004-06-13 20:20:40 +00001493 line_count = 0;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001494 idx = 0; // start with first index in block 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00001495 error = 0;
1496 buf->b_ml.ml_stack_top = 0;
1497 buf->b_ml.ml_stack = NULL;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001498 buf->b_ml.ml_stack_size = 0; // no stack yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00001499
1500 if (curbuf->b_ffname == NULL)
1501 cannot_open = TRUE;
1502 else
1503 cannot_open = FALSE;
1504
1505 serious_error = FALSE;
1506 for ( ; !got_int; line_breakcheck())
1507 {
1508 if (hp != NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001509 mf_put(mfp, hp, FALSE, FALSE); // release previous block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001510
1511 /*
1512 * get block
1513 */
1514 if ((hp = mf_get(mfp, (blocknr_T)bnum, page_count)) == NULL)
1515 {
1516 if (bnum == 1)
1517 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001518 semsg(_("E309: Unable to read block 1 from %s"), mfp->mf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001519 goto theend;
1520 }
1521 ++error;
1522 ml_append(lnum++, (char_u *)_("???MANY LINES MISSING"),
1523 (colnr_T)0, TRUE);
1524 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001525 else // there is a block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001526 {
1527 pp = (PTR_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001528 if (pp->pb_id == PTR_ID) // it is a pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001529 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001530 // check line count when using pointer block first time
Bram Moolenaar071d4272004-06-13 20:20:40 +00001531 if (idx == 0 && line_count != 0)
1532 {
1533 for (i = 0; i < (int)pp->pb_count; ++i)
1534 line_count -= pp->pb_pointer[i].pe_line_count;
1535 if (line_count != 0)
1536 {
1537 ++error;
1538 ml_append(lnum++, (char_u *)_("???LINE COUNT WRONG"),
1539 (colnr_T)0, TRUE);
1540 }
1541 }
1542
1543 if (pp->pb_count == 0)
1544 {
1545 ml_append(lnum++, (char_u *)_("???EMPTY BLOCK"),
1546 (colnr_T)0, TRUE);
1547 ++error;
1548 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001549 else if (idx < (int)pp->pb_count) // go a block deeper
Bram Moolenaar071d4272004-06-13 20:20:40 +00001550 {
1551 if (pp->pb_pointer[idx].pe_bnum < 0)
1552 {
1553 /*
1554 * Data block with negative block number.
1555 * Try to read lines from the original file.
1556 * This is slow, but it works.
1557 */
1558 if (!cannot_open)
1559 {
1560 line_count = pp->pb_pointer[idx].pe_line_count;
1561 if (readfile(curbuf->b_ffname, NULL, lnum,
1562 pp->pb_pointer[idx].pe_old_lnum - 1,
Bram Moolenaare13b9af2017-01-13 22:01:02 +01001563 line_count, NULL, 0) != OK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001564 cannot_open = TRUE;
1565 else
1566 lnum += line_count;
1567 }
1568 if (cannot_open)
1569 {
1570 ++error;
1571 ml_append(lnum++, (char_u *)_("???LINES MISSING"),
1572 (colnr_T)0, TRUE);
1573 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001574 ++idx; // get same block again for next index
Bram Moolenaar071d4272004-06-13 20:20:40 +00001575 continue;
1576 }
1577
1578 /*
1579 * going one block deeper in the tree
1580 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001581 if ((top = ml_add_stack(buf)) < 0) // new entry in stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00001582 {
1583 ++error;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001584 break; // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00001585 }
1586 ip = &(buf->b_ml.ml_stack[top]);
1587 ip->ip_bnum = bnum;
1588 ip->ip_index = idx;
1589
1590 bnum = pp->pb_pointer[idx].pe_bnum;
1591 line_count = pp->pb_pointer[idx].pe_line_count;
1592 page_count = pp->pb_pointer[idx].pe_page_count;
Bram Moolenaar986a0032011-06-13 01:07:27 +02001593 idx = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001594 continue;
1595 }
1596 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001597 else // not a pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001598 {
1599 dp = (DATA_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001600 if (dp->db_id != DATA_ID) // block id wrong
Bram Moolenaar071d4272004-06-13 20:20:40 +00001601 {
1602 if (bnum == 1)
1603 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001604 semsg(_("E310: Block 1 ID wrong (%s not a .swp file?)"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001605 mfp->mf_fname);
1606 goto theend;
1607 }
1608 ++error;
1609 ml_append(lnum++, (char_u *)_("???BLOCK MISSING"),
1610 (colnr_T)0, TRUE);
1611 }
1612 else
1613 {
1614 /*
1615 * it is a data block
1616 * Append all the lines in this block
1617 */
1618 has_error = FALSE;
1619 /*
1620 * check length of block
1621 * if wrong, use length in pointer block
1622 */
1623 if (page_count * mfp->mf_page_size != dp->db_txt_end)
1624 {
1625 ml_append(lnum++, (char_u *)_("??? from here until ???END lines may be messed up"),
1626 (colnr_T)0, TRUE);
1627 ++error;
1628 has_error = TRUE;
1629 dp->db_txt_end = page_count * mfp->mf_page_size;
1630 }
1631
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001632 // make sure there is a NUL at the end of the block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001633 *((char_u *)dp + dp->db_txt_end - 1) = NUL;
1634
1635 /*
1636 * check number of lines in block
1637 * if wrong, use count in data block
1638 */
1639 if (line_count != dp->db_line_count)
1640 {
1641 ml_append(lnum++, (char_u *)_("??? from here until ???END lines may have been inserted/deleted"),
1642 (colnr_T)0, TRUE);
1643 ++error;
1644 has_error = TRUE;
1645 }
1646
1647 for (i = 0; i < dp->db_line_count; ++i)
1648 {
1649 txt_start = (dp->db_index[i] & DB_INDEX_MASK);
Bram Moolenaar740885b2009-11-03 14:33:17 +00001650 if (txt_start <= (int)HEADER_SIZE
Bram Moolenaar071d4272004-06-13 20:20:40 +00001651 || txt_start >= (int)dp->db_txt_end)
1652 {
1653 p = (char_u *)"???";
1654 ++error;
1655 }
1656 else
1657 p = (char_u *)dp + txt_start;
1658 ml_append(lnum++, p, (colnr_T)0, TRUE);
1659 }
1660 if (has_error)
Bram Moolenaar740885b2009-11-03 14:33:17 +00001661 ml_append(lnum++, (char_u *)_("???END"),
1662 (colnr_T)0, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001663 }
1664 }
1665 }
1666
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001667 if (buf->b_ml.ml_stack_top == 0) // finished
Bram Moolenaar071d4272004-06-13 20:20:40 +00001668 break;
1669
1670 /*
1671 * go one block up in the tree
1672 */
1673 ip = &(buf->b_ml.ml_stack[--(buf->b_ml.ml_stack_top)]);
1674 bnum = ip->ip_bnum;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001675 idx = ip->ip_index + 1; // go to next index
Bram Moolenaar071d4272004-06-13 20:20:40 +00001676 page_count = 1;
1677 }
1678
1679 /*
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001680 * Compare the buffer contents with the original file. When they differ
1681 * set the 'modified' flag.
1682 * Lines 1 - lnum are the new contents.
1683 * Lines lnum + 1 to ml_line_count are the original contents.
1684 * Line ml_line_count + 1 in the dummy empty line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001685 */
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001686 if (orig_file_status != OK || curbuf->b_ml.ml_line_count != lnum * 2 + 1)
1687 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001688 // Recovering an empty file results in two lines and the first line is
1689 // empty. Don't set the modified flag then.
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001690 if (!(curbuf->b_ml.ml_line_count == 2 && *ml_get(1) == NUL))
1691 {
Bram Moolenaarec28d152019-05-11 18:36:34 +02001692 changed_internal();
Bram Moolenaar95c526e2017-02-25 14:59:34 +01001693 ++CHANGEDTICK(curbuf);
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001694 }
1695 }
1696 else
1697 {
1698 for (idx = 1; idx <= lnum; ++idx)
1699 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001700 // Need to copy one line, fetching the other one may flush it.
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001701 p = vim_strsave(ml_get(idx));
1702 i = STRCMP(p, ml_get(idx + lnum));
1703 vim_free(p);
1704 if (i != 0)
1705 {
Bram Moolenaarec28d152019-05-11 18:36:34 +02001706 changed_internal();
Bram Moolenaar95c526e2017-02-25 14:59:34 +01001707 ++CHANGEDTICK(curbuf);
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001708 break;
1709 }
1710 }
1711 }
1712
1713 /*
1714 * Delete the lines from the original file and the dummy line from the
1715 * empty buffer. These will now be after the last line in the buffer.
1716 */
1717 while (curbuf->b_ml.ml_line_count > lnum
1718 && !(curbuf->b_ml.ml_flags & ML_EMPTY))
Bram Moolenaarca70c072020-05-30 20:30:46 +02001719 ml_delete(curbuf->b_ml.ml_line_count);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001720 curbuf->b_flags |= BF_RECOVERED;
Bram Moolenaare3f50ad2021-06-09 12:33:40 +02001721 check_cursor();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001722
1723 recoverymode = FALSE;
1724 if (got_int)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001725 emsg(_("E311: Recovery Interrupted"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001726 else if (error)
1727 {
1728 ++no_wait_return;
Bram Moolenaar32526b32019-01-19 17:43:09 +01001729 msg(">>>>>>>>>>>>>");
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001730 emsg(_("E312: Errors detected while recovering; look for lines starting with ???"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001731 --no_wait_return;
Bram Moolenaar32526b32019-01-19 17:43:09 +01001732 msg(_("See \":help E312\" for more information."));
1733 msg(">>>>>>>>>>>>>");
Bram Moolenaar071d4272004-06-13 20:20:40 +00001734 }
1735 else
1736 {
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001737 if (curbuf->b_changed)
1738 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001739 msg(_("Recovery completed. You should check if everything is OK."));
1740 msg_puts(_("\n(You might want to write out this file under another name\n"));
1741 msg_puts(_("and run diff with the original file to check for changes)"));
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001742 }
1743 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001744 msg(_("Recovery completed. Buffer contents equals file contents."));
Bram Moolenaarf8835082020-11-09 21:04:17 +01001745 msg_puts(_("\nYou may want to delete the .swp file now."));
1746#if defined(UNIX) || defined(MSWIN)
Bram Moolenaarf52f0602021-03-10 21:26:37 +01001747 if (swapfile_process_running(b0p, fname_used))
Bram Moolenaarf8835082020-11-09 21:04:17 +01001748 {
1749 // Warn there could be an active Vim on the same file, the user may
1750 // want to kill it.
1751 msg_puts(_("\nNote: process STILL RUNNING: "));
1752 msg_outnum(char_to_long(b0p->b0_pid));
1753 }
1754#endif
1755 msg_puts("\n\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00001756 cmdline_row = msg_row;
1757 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001758#ifdef FEAT_CRYPT
1759 if (*buf->b_p_key != NUL && STRCMP(curbuf->b_p_key, buf->b_p_key) != 0)
1760 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001761 msg_puts(_("Using crypt key from swap file for the text file.\n"));
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001762 set_option_value((char_u *)"key", 0L, buf->b_p_key, OPT_LOCAL);
1763 }
1764#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001765 redraw_curbuf_later(NOT_VALID);
1766
1767theend:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001768 vim_free(fname_used);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001769 recoverymode = FALSE;
1770 if (mfp != NULL)
1771 {
1772 if (hp != NULL)
1773 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001774 mf_close(mfp, FALSE); // will also vim_free(mfp->mf_fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001775 }
Bram Moolenaardf88dda2007-01-09 13:34:50 +00001776 if (buf != NULL)
1777 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001778#ifdef FEAT_CRYPT
1779 if (buf->b_p_key != curbuf->b_p_key)
1780 free_string_option(buf->b_p_key);
Bram Moolenaar0ad014c2010-07-25 14:00:46 +02001781 free_string_option(buf->b_p_cm);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001782#endif
Bram Moolenaardf88dda2007-01-09 13:34:50 +00001783 vim_free(buf->b_ml.ml_stack);
1784 vim_free(buf);
1785 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001786 if (serious_error && called_from_main)
1787 ml_close(curbuf, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001788 else
1789 {
1790 apply_autocmds(EVENT_BUFREADPOST, NULL, curbuf->b_fname, FALSE, curbuf);
1791 apply_autocmds(EVENT_BUFWINENTER, NULL, curbuf->b_fname, FALSE, curbuf);
1792 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001793 return;
1794}
1795
1796/*
1797 * Find the names of swap files in current directory and the directory given
1798 * with the 'directory' option.
1799 *
1800 * Used to:
1801 * - list the swap files for "vim -r"
1802 * - count the number of swap files when recovering
1803 * - list the swap files when recovering
1804 * - find the name of the n'th swap file when recovering
1805 */
1806 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001807recover_names(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001808 char_u *fname, // base for swap file name
1809 int list, // when TRUE, list the swap file names
1810 int nr, // when non-zero, return nr'th swap file name
1811 char_u **fname_out) // result when "nr" > 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001812{
1813 int num_names;
1814 char_u *(names[6]);
1815 char_u *tail;
1816 char_u *p;
1817 int num_files;
1818 int file_count = 0;
1819 char_u **files;
1820 int i;
1821 char_u *dirp;
1822 char_u *dir_name;
Bram Moolenaar64354da2010-05-25 21:37:17 +02001823 char_u *fname_res = NULL;
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001824#ifdef HAVE_READLINK
1825 char_u fname_buf[MAXPATHL];
Bram Moolenaar64354da2010-05-25 21:37:17 +02001826#endif
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001827
Bram Moolenaar64354da2010-05-25 21:37:17 +02001828 if (fname != NULL)
1829 {
1830#ifdef HAVE_READLINK
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001831 // Expand symlink in the file name, because the swap file is created
1832 // with the actual file instead of with the symlink.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001833 if (resolve_symlink(fname, fname_buf) == OK)
1834 fname_res = fname_buf;
1835 else
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001836#endif
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001837 fname_res = fname;
Bram Moolenaar64354da2010-05-25 21:37:17 +02001838 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001839
1840 if (list)
1841 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001842 // use msg() to start the scrolling properly
Bram Moolenaar32526b32019-01-19 17:43:09 +01001843 msg(_("Swap files found:"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001844 msg_putchar('\n');
1845 }
1846
1847 /*
1848 * Do the loop for every directory in 'directory'.
1849 * First allocate some memory to put the directory name in.
1850 */
Bram Moolenaar964b3742019-05-24 18:54:09 +02001851 dir_name = alloc(STRLEN(p_dir) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001852 dirp = p_dir;
1853 while (dir_name != NULL && *dirp)
1854 {
1855 /*
1856 * Isolate a directory name from *dirp and put it in dir_name (we know
1857 * it is large enough, so use 31000 for length).
1858 * Advance dirp to next directory name.
1859 */
1860 (void)copy_option_part(&dirp, dir_name, 31000, ",");
1861
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001862 if (dir_name[0] == '.' && dir_name[1] == NUL) // check current dir
Bram Moolenaar071d4272004-06-13 20:20:40 +00001863 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001864 if (fname == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001865 {
1866#ifdef VMS
1867 names[0] = vim_strsave((char_u *)"*_sw%");
1868#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001869 names[0] = vim_strsave((char_u *)"*.sw?");
Bram Moolenaar071d4272004-06-13 20:20:40 +00001870#endif
Bram Moolenaar4f974752019-02-17 17:44:42 +01001871#if defined(UNIX) || defined(MSWIN)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001872 // For Unix names starting with a dot are special. MS-Windows
1873 // supports this too, on some file systems.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001874 names[1] = vim_strsave((char_u *)".*.sw?");
1875 names[2] = vim_strsave((char_u *)".sw?");
1876 num_names = 3;
1877#else
1878# ifdef VMS
1879 names[1] = vim_strsave((char_u *)".*_sw%");
1880 num_names = 2;
1881# else
1882 num_names = 1;
1883# endif
1884#endif
1885 }
1886 else
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001887 num_names = recov_file_names(names, fname_res, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001888 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001889 else // check directory dir_name
Bram Moolenaar071d4272004-06-13 20:20:40 +00001890 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001891 if (fname == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001892 {
1893#ifdef VMS
1894 names[0] = concat_fnames(dir_name, (char_u *)"*_sw%", TRUE);
1895#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001896 names[0] = concat_fnames(dir_name, (char_u *)"*.sw?", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001897#endif
Bram Moolenaar4f974752019-02-17 17:44:42 +01001898#if defined(UNIX) || defined(MSWIN)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001899 // For Unix names starting with a dot are special. MS-Windows
1900 // supports this too, on some file systems.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001901 names[1] = concat_fnames(dir_name, (char_u *)".*.sw?", TRUE);
1902 names[2] = concat_fnames(dir_name, (char_u *)".sw?", TRUE);
1903 num_names = 3;
1904#else
1905# ifdef VMS
1906 names[1] = concat_fnames(dir_name, (char_u *)".*_sw%", TRUE);
1907 num_names = 2;
1908# else
1909 num_names = 1;
1910# endif
1911#endif
1912 }
1913 else
1914 {
Bram Moolenaar4f974752019-02-17 17:44:42 +01001915#if defined(UNIX) || defined(MSWIN)
Bram Moolenaarb113c3a2017-02-28 21:26:17 +01001916 int len = (int)STRLEN(dir_name);
Bram Moolenaarc525e3a2017-02-18 16:59:02 +01001917
1918 p = dir_name + len;
1919 if (after_pathsep(dir_name, p) && len > 1 && p[-1] == p[-2])
Bram Moolenaar071d4272004-06-13 20:20:40 +00001920 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001921 // Ends with '//', Use Full path for swap name
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001922 tail = make_percent_swname(dir_name, fname_res);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001923 }
1924 else
1925#endif
1926 {
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001927 tail = gettail(fname_res);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001928 tail = concat_fnames(dir_name, tail, TRUE);
1929 }
1930 if (tail == NULL)
1931 num_names = 0;
1932 else
1933 {
1934 num_names = recov_file_names(names, tail, FALSE);
1935 vim_free(tail);
1936 }
1937 }
1938 }
1939
Bram Moolenaar0d3cb732019-05-18 13:05:18 +02001940 // check for out-of-memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00001941 for (i = 0; i < num_names; ++i)
1942 {
1943 if (names[i] == NULL)
1944 {
1945 for (i = 0; i < num_names; ++i)
1946 vim_free(names[i]);
1947 num_names = 0;
1948 }
1949 }
1950 if (num_names == 0)
1951 num_files = 0;
1952 else if (expand_wildcards(num_names, names, &num_files, &files,
Bram Moolenaar99499b12019-05-23 21:35:48 +02001953 EW_NOTENV|EW_KEEPALL|EW_FILE|EW_SILENT) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001954 num_files = 0;
1955
1956 /*
1957 * When no swap file found, wildcard expansion might have failed (e.g.
1958 * not able to execute the shell).
1959 * Try finding a swap file by simply adding ".swp" to the file name.
1960 */
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001961 if (*dirp == NUL && file_count + num_files == 0 && fname != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001962 {
Bram Moolenaar8767f522016-07-01 17:17:39 +02001963 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001964 char_u *swapname;
1965
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001966 swapname = modname(fname_res,
Bram Moolenaare60acc12011-05-10 16:41:25 +02001967#if defined(VMS)
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001968 (char_u *)"_swp", FALSE
Bram Moolenaar071d4272004-06-13 20:20:40 +00001969#else
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001970 (char_u *)".swp", TRUE
Bram Moolenaar071d4272004-06-13 20:20:40 +00001971#endif
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001972 );
Bram Moolenaar071d4272004-06-13 20:20:40 +00001973 if (swapname != NULL)
1974 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001975 if (mch_stat((char *)swapname, &st) != -1) // It exists!
Bram Moolenaar071d4272004-06-13 20:20:40 +00001976 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001977 files = ALLOC_ONE(char_u *);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001978 if (files != NULL)
1979 {
1980 files[0] = swapname;
1981 swapname = NULL;
1982 num_files = 1;
1983 }
1984 }
1985 vim_free(swapname);
1986 }
1987 }
1988
1989 /*
1990 * remove swapfile name of the current buffer, it must be ignored
1991 */
1992 if (curbuf->b_ml.ml_mfp != NULL
1993 && (p = curbuf->b_ml.ml_mfp->mf_fname) != NULL)
1994 {
1995 for (i = 0; i < num_files; ++i)
Bram Moolenaar99499b12019-05-23 21:35:48 +02001996 // Do not expand wildcards, on windows would try to expand
1997 // "%tmp%" in "%tmp%file".
1998 if (fullpathcmp(p, files[i], TRUE, FALSE) & FPC_SAME)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001999 {
Bram Moolenaar99499b12019-05-23 21:35:48 +02002000 // Remove the name from files[i]. Move further entries
2001 // down. When the array becomes empty free it here, since
2002 // FreeWild() won't be called below.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002003 vim_free(files[i]);
Bram Moolenaar9439cdd2009-04-22 13:39:36 +00002004 if (--num_files == 0)
2005 vim_free(files);
2006 else
2007 for ( ; i < num_files; ++i)
2008 files[i] = files[i + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002009 }
2010 }
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00002011 if (nr > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002012 {
2013 file_count += num_files;
2014 if (nr <= file_count)
2015 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002016 *fname_out = vim_strsave(
2017 files[nr - 1 + num_files - file_count]);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002018 dirp = (char_u *)""; // stop searching
Bram Moolenaar071d4272004-06-13 20:20:40 +00002019 }
2020 }
2021 else if (list)
2022 {
2023 if (dir_name[0] == '.' && dir_name[1] == NUL)
2024 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002025 if (fname == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002026 msg_puts(_(" In current directory:\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002027 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002028 msg_puts(_(" Using specified name:\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002029 }
2030 else
2031 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002032 msg_puts(_(" In directory "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002033 msg_home_replace(dir_name);
Bram Moolenaar32526b32019-01-19 17:43:09 +01002034 msg_puts(":\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00002035 }
2036
2037 if (num_files)
2038 {
2039 for (i = 0; i < num_files; ++i)
2040 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002041 // print the swap file name
Bram Moolenaar071d4272004-06-13 20:20:40 +00002042 msg_outnum((long)++file_count);
Bram Moolenaar32526b32019-01-19 17:43:09 +01002043 msg_puts(". ");
2044 msg_puts((char *)gettail(files[i]));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002045 msg_putchar('\n');
2046 (void)swapfile_info(files[i]);
2047 }
2048 }
2049 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002050 msg_puts(_(" -- none --\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002051 out_flush();
2052 }
2053 else
2054 file_count += num_files;
2055
2056 for (i = 0; i < num_names; ++i)
2057 vim_free(names[i]);
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00002058 if (num_files > 0)
2059 FreeWild(num_files, files);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002060 }
2061 vim_free(dir_name);
2062 return file_count;
2063}
2064
Bram Moolenaar4f974752019-02-17 17:44:42 +01002065#if defined(UNIX) || defined(MSWIN) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002066/*
Bram Moolenaarb782ba42018-08-07 21:39:28 +02002067 * Need _very_ long file names.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002068 * Append the full path to name with path separators made into percent
2069 * signs, to dir. An unnamed buffer is handled as "" (<currentdir>/"")
2070 */
Bram Moolenaarb782ba42018-08-07 21:39:28 +02002071 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002072make_percent_swname(char_u *dir, char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002073{
Bram Moolenaarb782ba42018-08-07 21:39:28 +02002074 char_u *d = NULL, *s, *f;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002075
Bram Moolenaarb782ba42018-08-07 21:39:28 +02002076 f = fix_fname(name != NULL ? name : (char_u *)"");
Bram Moolenaar071d4272004-06-13 20:20:40 +00002077 if (f != NULL)
2078 {
Bram Moolenaar964b3742019-05-24 18:54:09 +02002079 s = alloc(STRLEN(f) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002080 if (s != NULL)
2081 {
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002082 STRCPY(s, f);
Bram Moolenaar91acfff2017-03-12 19:22:36 +01002083 for (d = s; *d != NUL; MB_PTR_ADV(d))
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002084 if (vim_ispathsep(*d))
2085 *d = '%';
Bram Moolenaar071d4272004-06-13 20:20:40 +00002086 d = concat_fnames(dir, s, TRUE);
2087 vim_free(s);
2088 }
2089 vim_free(f);
2090 }
2091 return d;
2092}
2093#endif
2094
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02002095#if (defined(UNIX) || defined(VMS) || defined(MSWIN)) \
2096 && (defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG))
2097# define HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar071d4272004-06-13 20:20:40 +00002098static int process_still_running;
2099#endif
2100
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002101#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002102/*
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002103 * Return information found in swapfile "fname" in dictionary "d".
2104 * This is used by the swapinfo() function.
2105 */
2106 void
2107get_b0_dict(char_u *fname, dict_T *d)
2108{
2109 int fd;
2110 struct block0 b0;
2111
2112 if ((fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0)) >= 0)
2113 {
2114 if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0))
2115 {
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002116 if (ml_check_b0_id(&b0) == FAIL)
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002117 dict_add_string(d, "error", (char_u *)"Not a swap file");
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002118 else if (b0_magic_wrong(&b0))
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002119 dict_add_string(d, "error", (char_u *)"Magic number mismatch");
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002120 else
2121 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002122 // we have swap information
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002123 dict_add_string_len(d, "version", b0.b0_version, 10);
2124 dict_add_string_len(d, "user", b0.b0_uname, B0_UNAME_SIZE);
2125 dict_add_string_len(d, "host", b0.b0_hname, B0_HNAME_SIZE);
2126 dict_add_string_len(d, "fname", b0.b0_fname, B0_FNAME_SIZE_ORG);
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002127
2128 dict_add_number(d, "pid", char_to_long(b0.b0_pid));
2129 dict_add_number(d, "mtime", char_to_long(b0.b0_mtime));
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002130 dict_add_number(d, "dirty", b0.b0_dirty ? 1 : 0);
2131# ifdef CHECK_INODE
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002132 dict_add_number(d, "inode", char_to_long(b0.b0_ino));
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002133# endif
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002134 }
2135 }
2136 else
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002137 dict_add_string(d, "error", (char_u *)"Cannot read file");
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002138 close(fd);
2139 }
2140 else
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002141 dict_add_string(d, "error", (char_u *)"Cannot open file");
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002142}
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002143#endif
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002144
2145/*
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00002146 * Give information about an existing swap file.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002147 * Returns timestamp (0 when unknown).
2148 */
2149 static time_t
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002150swapfile_info(char_u *fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002151{
Bram Moolenaar8767f522016-07-01 17:17:39 +02002152 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002153 int fd;
2154 struct block0 b0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002155#ifdef UNIX
2156 char_u uname[B0_UNAME_SIZE];
2157#endif
2158
Bram Moolenaar63d25552019-05-10 21:28:38 +02002159 // print the swap file date
Bram Moolenaar071d4272004-06-13 20:20:40 +00002160 if (mch_stat((char *)fname, &st) != -1)
2161 {
2162#ifdef UNIX
Bram Moolenaar63d25552019-05-10 21:28:38 +02002163 // print name of owner of the file
Bram Moolenaar071d4272004-06-13 20:20:40 +00002164 if (mch_get_uname(st.st_uid, uname, B0_UNAME_SIZE) == OK)
2165 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002166 msg_puts(_(" owned by: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002167 msg_outtrans(uname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01002168 msg_puts(_(" dated: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002169 }
2170 else
2171#endif
Bram Moolenaar32526b32019-01-19 17:43:09 +01002172 msg_puts(_(" dated: "));
Bram Moolenaar63d25552019-05-10 21:28:38 +02002173 msg_puts(get_ctime(st.st_mtime, TRUE));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002174 }
Bram Moolenaar63d25552019-05-10 21:28:38 +02002175 else
2176 st.st_mtime = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002177
2178 /*
2179 * print the original file name
2180 */
2181 fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
2182 if (fd >= 0)
2183 {
Bram Moolenaar540fc6f2010-12-17 16:27:16 +01002184 if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002185 {
2186 if (STRNCMP(b0.b0_version, "VIM 3.0", 7) == 0)
2187 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002188 msg_puts(_(" [from Vim version 3.0]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002189 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002190 else if (ml_check_b0_id(&b0) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002191 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002192 msg_puts(_(" [does not look like a Vim swap file]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002193 }
2194 else
2195 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002196 msg_puts(_(" file name: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002197 if (b0.b0_fname[0] == NUL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002198 msg_puts(_("[No Name]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002199 else
2200 msg_outtrans(b0.b0_fname);
2201
Bram Moolenaar32526b32019-01-19 17:43:09 +01002202 msg_puts(_("\n modified: "));
2203 msg_puts(b0.b0_dirty ? _("YES") : _("no"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002204
2205 if (*(b0.b0_uname) != NUL)
2206 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002207 msg_puts(_("\n user name: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002208 msg_outtrans(b0.b0_uname);
2209 }
2210
2211 if (*(b0.b0_hname) != NUL)
2212 {
2213 if (*(b0.b0_uname) != NUL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002214 msg_puts(_(" host name: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002215 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002216 msg_puts(_("\n host name: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002217 msg_outtrans(b0.b0_hname);
2218 }
2219
2220 if (char_to_long(b0.b0_pid) != 0L)
2221 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002222 msg_puts(_("\n process ID: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002223 msg_outnum(char_to_long(b0.b0_pid));
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002224#if defined(UNIX) || defined(MSWIN)
Bram Moolenaarf52f0602021-03-10 21:26:37 +01002225 if (swapfile_process_running(&b0, fname))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002226 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002227 msg_puts(_(" (STILL RUNNING)"));
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02002228# ifdef HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar071d4272004-06-13 20:20:40 +00002229 process_still_running = TRUE;
2230# endif
2231 }
2232#endif
2233 }
2234
2235 if (b0_magic_wrong(&b0))
2236 {
Bram Moolenaar48e330a2016-02-23 14:53:34 +01002237#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002238 if (STRNCMP(b0.b0_hname, "PC ", 3) == 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002239 msg_puts(_("\n [not usable with this version of Vim]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002240 else
2241#endif
Bram Moolenaar32526b32019-01-19 17:43:09 +01002242 msg_puts(_("\n [not usable on this computer]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002243 }
2244 }
2245 }
2246 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002247 msg_puts(_(" [cannot be read]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002248 close(fd);
2249 }
2250 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002251 msg_puts(_(" [cannot be opened]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002252 msg_putchar('\n');
2253
Bram Moolenaar63d25552019-05-10 21:28:38 +02002254 return st.st_mtime;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002255}
2256
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002257/*
2258 * Return TRUE if the swap file looks OK and there are no changes, thus it can
2259 * be safely deleted.
2260 */
2261 static time_t
2262swapfile_unchanged(char_u *fname)
2263{
2264 stat_T st;
2265 int fd;
2266 struct block0 b0;
2267 int ret = TRUE;
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002268
2269 // must be able to stat the swap file
2270 if (mch_stat((char *)fname, &st) == -1)
2271 return FALSE;
2272
2273 // must be able to read the first block
2274 fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
2275 if (fd < 0)
2276 return FALSE;
2277 if (read_eintr(fd, &b0, sizeof(b0)) != sizeof(b0))
2278 {
2279 close(fd);
2280 return FALSE;
2281 }
2282
2283 // the ID and magic number must be correct
2284 if (ml_check_b0_id(&b0) == FAIL|| b0_magic_wrong(&b0))
2285 ret = FALSE;
2286
2287 // must be unchanged
2288 if (b0.b0_dirty)
2289 ret = FALSE;
2290
2291#if defined(UNIX) || defined(MSWIN)
Bram Moolenaarf8835082020-11-09 21:04:17 +01002292 // Host name must be known and must equal the current host name, otherwise
2293 // comparing pid is meaningless.
2294 if (*(b0.b0_hname) == NUL)
2295 {
2296 ret = FALSE;
2297 }
2298 else
2299 {
2300 char_u hostname[B0_HNAME_SIZE];
2301
2302 mch_get_host_name(hostname, B0_HNAME_SIZE);
2303 hostname[B0_HNAME_SIZE - 1] = NUL;
Bram Moolenaare79cdb62020-11-21 13:51:16 +01002304 b0.b0_hname[B0_HNAME_SIZE - 1] = NUL; // in case of corruption
Bram Moolenaarf8835082020-11-09 21:04:17 +01002305 if (STRICMP(b0.b0_hname, hostname) != 0)
2306 ret = FALSE;
2307 }
2308
Bram Moolenaar32aa1022019-11-02 22:54:41 +01002309 // process must be known and not be running
Bram Moolenaarf52f0602021-03-10 21:26:37 +01002310 if (char_to_long(b0.b0_pid) == 0L || swapfile_process_running(&b0, fname))
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002311 ret = FALSE;
2312#endif
2313
Bram Moolenaarf8835082020-11-09 21:04:17 +01002314 // We do not check the user, it should be irrelevant for whether the swap
2315 // file is still useful.
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002316
2317 close(fd);
2318 return ret;
2319}
2320
Bram Moolenaar071d4272004-06-13 20:20:40 +00002321 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002322recov_file_names(char_u **names, char_u *path, int prepend_dot)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002323{
2324 int num_names;
2325
Bram Moolenaar071d4272004-06-13 20:20:40 +00002326 /*
2327 * (Win32 and Win64) never short names, but do prepend a dot.
2328 * (Not MS-DOS or Win32 or Win64) maybe short name, maybe not: Try both.
2329 * Only use the short name if it is different.
2330 */
2331 char_u *p;
2332 int i;
Bram Moolenaar4f974752019-02-17 17:44:42 +01002333# ifndef MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002334 int shortname = curbuf->b_shortname;
2335
2336 curbuf->b_shortname = FALSE;
2337# endif
2338
2339 num_names = 0;
2340
2341 /*
2342 * May also add the file name with a dot prepended, for swap file in same
2343 * dir as original file.
2344 */
2345 if (prepend_dot)
2346 {
2347 names[num_names] = modname(path, (char_u *)".sw?", TRUE);
2348 if (names[num_names] == NULL)
2349 goto end;
2350 ++num_names;
2351 }
2352
2353 /*
2354 * Form the normal swap file name pattern by appending ".sw?".
2355 */
2356#ifdef VMS
2357 names[num_names] = concat_fnames(path, (char_u *)"_sw%", FALSE);
2358#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002359 names[num_names] = concat_fnames(path, (char_u *)".sw?", FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002360#endif
2361 if (names[num_names] == NULL)
2362 goto end;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002363 if (num_names >= 1) // check if we have the same name twice
Bram Moolenaar071d4272004-06-13 20:20:40 +00002364 {
2365 p = names[num_names - 1];
2366 i = (int)STRLEN(names[num_names - 1]) - (int)STRLEN(names[num_names]);
2367 if (i > 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002368 p += i; // file name has been expanded to full path
Bram Moolenaar071d4272004-06-13 20:20:40 +00002369
2370 if (STRCMP(p, names[num_names]) != 0)
2371 ++num_names;
2372 else
2373 vim_free(names[num_names]);
2374 }
2375 else
2376 ++num_names;
2377
Bram Moolenaar4f974752019-02-17 17:44:42 +01002378# ifndef MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002379 /*
2380 * Also try with 'shortname' set, in case the file is on a DOS filesystem.
2381 */
2382 curbuf->b_shortname = TRUE;
2383#ifdef VMS
2384 names[num_names] = modname(path, (char_u *)"_sw%", FALSE);
2385#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002386 names[num_names] = modname(path, (char_u *)".sw?", FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002387#endif
2388 if (names[num_names] == NULL)
2389 goto end;
2390
2391 /*
2392 * Remove the one from 'shortname', if it's the same as with 'noshortname'.
2393 */
2394 p = names[num_names];
2395 i = STRLEN(names[num_names]) - STRLEN(names[num_names - 1]);
2396 if (i > 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002397 p += i; // file name has been expanded to full path
Bram Moolenaar071d4272004-06-13 20:20:40 +00002398 if (STRCMP(names[num_names - 1], p) == 0)
2399 vim_free(names[num_names]);
2400 else
2401 ++num_names;
2402# endif
2403
2404end:
Bram Moolenaar4f974752019-02-17 17:44:42 +01002405# ifndef MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002406 curbuf->b_shortname = shortname;
2407# endif
2408
Bram Moolenaar071d4272004-06-13 20:20:40 +00002409 return num_names;
2410}
2411
2412/*
2413 * sync all memlines
2414 *
2415 * If 'check_file' is TRUE, check if original file exists and was not changed.
2416 * If 'check_char' is TRUE, stop syncing when character becomes available, but
2417 * always sync at least one block.
2418 */
2419 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002420ml_sync_all(int check_file, int check_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002421{
2422 buf_T *buf;
Bram Moolenaar8767f522016-07-01 17:17:39 +02002423 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002424
Bram Moolenaar29323592016-07-24 22:04:11 +02002425 FOR_ALL_BUFFERS(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002426 {
Christian Brabandtf573c6e2021-06-20 14:02:16 +02002427 if (buf->b_ml.ml_mfp == NULL
2428 || buf->b_ml.ml_mfp->mf_fname == NULL
2429 || buf->b_ml.ml_mfp->mf_fd < 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002430 continue; // no file
Bram Moolenaar071d4272004-06-13 20:20:40 +00002431
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002432 ml_flush_line(buf); // flush buffered line
2433 // flush locked block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002434 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH);
2435 if (bufIsChanged(buf) && check_file && mf_need_trans(buf->b_ml.ml_mfp)
2436 && buf->b_ffname != NULL)
2437 {
2438 /*
2439 * If the original file does not exist anymore or has been changed
2440 * call ml_preserve() to get rid of all negative numbered blocks.
2441 */
2442 if (mch_stat((char *)buf->b_ffname, &st) == -1
2443 || st.st_mtime != buf->b_mtime_read
Bram Moolenaar914703b2010-05-31 21:59:46 +02002444 || st.st_size != buf->b_orig_size)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002445 {
2446 ml_preserve(buf, FALSE);
2447 did_check_timestamps = FALSE;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002448 need_check_timestamps = TRUE; // give message later
Bram Moolenaar071d4272004-06-13 20:20:40 +00002449 }
2450 }
2451 if (buf->b_ml.ml_mfp->mf_dirty)
2452 {
2453 (void)mf_sync(buf->b_ml.ml_mfp, (check_char ? MFS_STOP : 0)
2454 | (bufIsChanged(buf) ? MFS_FLUSH : 0));
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002455 if (check_char && ui_char_avail()) // character available now
Bram Moolenaar071d4272004-06-13 20:20:40 +00002456 break;
2457 }
2458 }
2459}
2460
2461/*
2462 * sync one buffer, including negative blocks
2463 *
2464 * after this all the blocks are in the swap file
2465 *
2466 * Used for the :preserve command and when the original file has been
2467 * changed or deleted.
2468 *
2469 * when message is TRUE the success of preserving is reported
2470 */
2471 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002472ml_preserve(buf_T *buf, int message)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002473{
2474 bhdr_T *hp;
2475 linenr_T lnum;
2476 memfile_T *mfp = buf->b_ml.ml_mfp;
2477 int status;
2478 int got_int_save = got_int;
2479
2480 if (mfp == NULL || mfp->mf_fname == NULL)
2481 {
2482 if (message)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002483 emsg(_("E313: Cannot preserve, there is no swap file"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002484 return;
2485 }
2486
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002487 // We only want to stop when interrupted here, not when interrupted
2488 // before.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002489 got_int = FALSE;
2490
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002491 ml_flush_line(buf); // flush buffered line
2492 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush locked block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002493 status = mf_sync(mfp, MFS_ALL | MFS_FLUSH);
2494
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002495 // stack is invalid after mf_sync(.., MFS_ALL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002496 buf->b_ml.ml_stack_top = 0;
2497
2498 /*
2499 * Some of the data blocks may have been changed from negative to
2500 * positive block number. In that case the pointer blocks need to be
2501 * updated.
2502 *
2503 * We don't know in which pointer block the references are, so we visit
2504 * all data blocks until there are no more translations to be done (or
2505 * we hit the end of the file, which can only happen in case a write fails,
2506 * e.g. when file system if full).
2507 * ml_find_line() does the work by translating the negative block numbers
2508 * when getting the first line of each data block.
2509 */
2510 if (mf_need_trans(mfp) && !got_int)
2511 {
2512 lnum = 1;
2513 while (mf_need_trans(mfp) && lnum <= buf->b_ml.ml_line_count)
2514 {
2515 hp = ml_find_line(buf, lnum, ML_FIND);
2516 if (hp == NULL)
2517 {
2518 status = FAIL;
2519 goto theend;
2520 }
2521 CHECK(buf->b_ml.ml_locked_low != lnum, "low != lnum");
2522 lnum = buf->b_ml.ml_locked_high + 1;
2523 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002524 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush locked block
2525 // sync the updated pointer blocks
Bram Moolenaar071d4272004-06-13 20:20:40 +00002526 if (mf_sync(mfp, MFS_ALL | MFS_FLUSH) == FAIL)
2527 status = FAIL;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002528 buf->b_ml.ml_stack_top = 0; // stack is invalid now
Bram Moolenaar071d4272004-06-13 20:20:40 +00002529 }
2530theend:
2531 got_int |= got_int_save;
2532
2533 if (message)
2534 {
2535 if (status == OK)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002536 msg(_("File preserved"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002537 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002538 emsg(_("E314: Preserve failed"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002539 }
2540}
2541
2542/*
2543 * NOTE: The pointer returned by the ml_get_*() functions only remains valid
2544 * until the next call!
2545 * line1 = ml_get(1);
2546 * line2 = ml_get(2); // line1 is now invalid!
2547 * Make a copy of the line if necessary.
2548 */
2549/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002550 * Return a pointer to a (read-only copy of a) line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002551 *
2552 * On failure an error message is given and IObuff is returned (to avoid
2553 * having to check for error everywhere).
2554 */
2555 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002556ml_get(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002557{
2558 return ml_get_buf(curbuf, lnum, FALSE);
2559}
2560
2561/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002562 * Return pointer to position "pos".
Bram Moolenaar071d4272004-06-13 20:20:40 +00002563 */
2564 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002565ml_get_pos(pos_T *pos)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002566{
2567 return (ml_get_buf(curbuf, pos->lnum, FALSE) + pos->col);
2568}
2569
2570/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002571 * Return pointer to cursor line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002572 */
2573 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002574ml_get_curline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002575{
2576 return ml_get_buf(curbuf, curwin->w_cursor.lnum, FALSE);
2577}
2578
2579/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002580 * Return pointer to cursor position.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002581 */
2582 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002583ml_get_cursor(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002584{
2585 return (ml_get_buf(curbuf, curwin->w_cursor.lnum, FALSE) +
2586 curwin->w_cursor.col);
2587}
2588
2589/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002590 * Return a pointer to a line in a specific buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +00002591 *
2592 * "will_change": if TRUE mark the buffer dirty (chars in the line will be
2593 * changed)
2594 */
2595 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002596ml_get_buf(
2597 buf_T *buf,
2598 linenr_T lnum,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002599 int will_change) // line will be changed
Bram Moolenaar071d4272004-06-13 20:20:40 +00002600{
Bram Moolenaarad40f022007-02-13 03:01:39 +00002601 bhdr_T *hp;
2602 DATA_BL *dp;
Bram Moolenaarad40f022007-02-13 03:01:39 +00002603 static int recursive = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002604
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002605 if (lnum > buf->b_ml.ml_line_count) // invalid line number
Bram Moolenaar071d4272004-06-13 20:20:40 +00002606 {
Bram Moolenaarad40f022007-02-13 03:01:39 +00002607 if (recursive == 0)
2608 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002609 // Avoid giving this message for a recursive call, may happen when
2610 // the GUI redraws part of the text.
Bram Moolenaarad40f022007-02-13 03:01:39 +00002611 ++recursive;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002612 siemsg(_("E315: ml_get: invalid lnum: %ld"), lnum);
Bram Moolenaarad40f022007-02-13 03:01:39 +00002613 --recursive;
2614 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002615errorret:
2616 STRCPY(IObuff, "???");
Bram Moolenaaradfde112019-05-25 22:11:45 +02002617 buf->b_ml.ml_line_len = 4;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002618 return IObuff;
2619 }
Bram Moolenaaradfde112019-05-25 22:11:45 +02002620 if (lnum <= 0) // pretend line 0 is line 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00002621 lnum = 1;
2622
Bram Moolenaaradfde112019-05-25 22:11:45 +02002623 if (buf->b_ml.ml_mfp == NULL) // there are no lines
2624 {
2625 buf->b_ml.ml_line_len = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002626 return (char_u *)"";
Bram Moolenaaradfde112019-05-25 22:11:45 +02002627 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002628
Bram Moolenaar37d619f2010-03-10 14:46:26 +01002629 /*
2630 * See if it is the same line as requested last time.
2631 * Otherwise may need to flush last used line.
2632 * Don't use the last used line when 'swapfile' is reset, need to load all
2633 * blocks.
2634 */
Bram Moolenaar47b8b152007-02-07 02:41:57 +00002635 if (buf->b_ml.ml_line_lnum != lnum || mf_dont_release)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002636 {
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002637 unsigned start, end;
2638 colnr_T len;
2639 int idx;
2640
Bram Moolenaar071d4272004-06-13 20:20:40 +00002641 ml_flush_line(buf);
2642
2643 /*
2644 * Find the data block containing the line.
2645 * This also fills the stack with the blocks from the root to the data
2646 * block and releases any locked block.
2647 */
2648 if ((hp = ml_find_line(buf, lnum, ML_FIND)) == NULL)
2649 {
Bram Moolenaarad40f022007-02-13 03:01:39 +00002650 if (recursive == 0)
2651 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002652 // Avoid giving this message for a recursive call, may happen
2653 // when the GUI redraws part of the text.
Bram Moolenaarad40f022007-02-13 03:01:39 +00002654 ++recursive;
Bram Moolenaarcb868932019-10-26 20:56:21 +02002655 get_trans_bufname(buf);
2656 shorten_dir(NameBuff);
2657 siemsg(_("E316: ml_get: cannot find line %ld in buffer %d %s"),
2658 lnum, buf->b_fnum, NameBuff);
Bram Moolenaarad40f022007-02-13 03:01:39 +00002659 --recursive;
2660 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002661 goto errorret;
2662 }
2663
2664 dp = (DATA_BL *)(hp->bh_data);
2665
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002666 idx = lnum - buf->b_ml.ml_locked_low;
2667 start = ((dp->db_index[idx]) & DB_INDEX_MASK);
2668 // The text ends where the previous line starts. The first line ends
2669 // at the end of the block.
2670 if (idx == 0)
2671 end = dp->db_txt_end;
2672 else
2673 end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK);
2674 len = end - start;
2675
2676 buf->b_ml.ml_line_ptr = (char_u *)dp + start;
2677 buf->b_ml.ml_line_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002678 buf->b_ml.ml_line_lnum = lnum;
2679 buf->b_ml.ml_flags &= ~ML_LINE_DIRTY;
2680 }
2681 if (will_change)
2682 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
2683
2684 return buf->b_ml.ml_line_ptr;
2685}
2686
2687/*
2688 * Check if a line that was just obtained by a call to ml_get
2689 * is in allocated memory.
2690 */
2691 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002692ml_line_alloced(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002693{
2694 return (curbuf->b_ml.ml_flags & ML_LINE_DIRTY);
2695}
2696
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002697#ifdef FEAT_PROP_POPUP
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002698/*
2699 * Add text properties that continue from the previous line.
2700 */
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002701 static void
2702add_text_props_for_append(
2703 buf_T *buf,
2704 linenr_T lnum,
2705 char_u **line,
2706 int *len,
2707 char_u **tofree)
2708{
2709 int round;
2710 int new_prop_count = 0;
2711 int count;
2712 int n;
2713 char_u *props;
Bram Moolenaarea781452019-09-04 18:53:12 +02002714 int new_len = 0; // init for gcc
Bram Moolenaar8f13d822020-09-12 21:04:23 +02002715 char_u *new_line = NULL;
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002716 textprop_T prop;
2717
2718 // Make two rounds:
2719 // 1. calculate the extra space needed
2720 // 2. allocate the space and fill it
2721 for (round = 1; round <= 2; ++round)
2722 {
2723 if (round == 2)
2724 {
2725 if (new_prop_count == 0)
2726 return; // nothing to do
2727 new_len = *len + new_prop_count * sizeof(textprop_T);
Bram Moolenaar964b3742019-05-24 18:54:09 +02002728 new_line = alloc(new_len);
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002729 if (new_line == NULL)
2730 return;
2731 mch_memmove(new_line, *line, *len);
2732 new_prop_count = 0;
2733 }
2734
2735 // Get the line above to find any props that continue in the next
2736 // line.
2737 count = get_text_props(buf, lnum, &props, FALSE);
2738 for (n = 0; n < count; ++n)
2739 {
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002740 mch_memmove(&prop, props + n * sizeof(textprop_T),
2741 sizeof(textprop_T));
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002742 if (prop.tp_flags & TP_FLAG_CONT_NEXT)
2743 {
2744 if (round == 2)
2745 {
2746 prop.tp_flags |= TP_FLAG_CONT_PREV;
2747 prop.tp_col = 1;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002748 prop.tp_len = *len; // not exactly the right length
2749 mch_memmove(new_line + *len + new_prop_count
2750 * sizeof(textprop_T), &prop, sizeof(textprop_T));
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002751 }
2752 ++new_prop_count;
2753 }
2754 }
2755 }
2756 *line = new_line;
2757 *tofree = new_line;
2758 *len = new_len;
2759}
2760#endif
2761
Bram Moolenaar071d4272004-06-13 20:20:40 +00002762 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002763ml_append_int(
2764 buf_T *buf,
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002765 linenr_T lnum, // append after this line (can be 0)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002766 char_u *line_arg, // text of the new line
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002767 colnr_T len_arg, // length of line, including NUL, or 0
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002768 int flags) // ML_APPEND_ flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00002769{
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002770 char_u *line = line_arg;
2771 colnr_T len = len_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002772 int i;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002773 int line_count; // number of indexes in current block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002774 int offset;
2775 int from, to;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002776 int space_needed; // space needed for new line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002777 int page_size;
2778 int page_count;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002779 int db_idx; // index for lnum in data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002780 bhdr_T *hp;
2781 memfile_T *mfp;
2782 DATA_BL *dp;
2783 PTR_BL *pp;
2784 infoptr_T *ip;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002785#ifdef FEAT_PROP_POPUP
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002786 char_u *tofree = NULL;
2787#endif
2788 int ret = FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002789
Bram Moolenaar071d4272004-06-13 20:20:40 +00002790 if (lnum > buf->b_ml.ml_line_count || buf->b_ml.ml_mfp == NULL)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002791 return FAIL; // lnum out of range
Bram Moolenaar071d4272004-06-13 20:20:40 +00002792
2793 if (lowest_marked && lowest_marked > lnum)
2794 lowest_marked = lnum + 1;
2795
2796 if (len == 0)
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002797 len = (colnr_T)STRLEN(line) + 1; // space needed for the text
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002798
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002799#ifdef FEAT_PROP_POPUP
Bram Moolenaar840f91f2021-05-26 22:32:10 +02002800 if (curbuf->b_has_textprop && lnum > 0
2801 && !(flags & (ML_APPEND_UNDO | ML_APPEND_NOPROP)))
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002802 // Add text properties that continue from the previous line.
2803 add_text_props_for_append(buf, lnum, &line, &len, &tofree);
2804#endif
2805
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002806 space_needed = len + INDEX_SIZE; // space needed for text + index
Bram Moolenaar071d4272004-06-13 20:20:40 +00002807
2808 mfp = buf->b_ml.ml_mfp;
2809 page_size = mfp->mf_page_size;
2810
2811/*
2812 * find the data block containing the previous line
2813 * This also fills the stack with the blocks from the root to the data block
2814 * This also releases any locked block.
2815 */
2816 if ((hp = ml_find_line(buf, lnum == 0 ? (linenr_T)1 : lnum,
2817 ML_INSERT)) == NULL)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002818 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002819
2820 buf->b_ml.ml_flags &= ~ML_EMPTY;
2821
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002822 if (lnum == 0) // got line one instead, correct db_idx
2823 db_idx = -1; // careful, it is negative!
Bram Moolenaar071d4272004-06-13 20:20:40 +00002824 else
2825 db_idx = lnum - buf->b_ml.ml_locked_low;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002826 // get line count before the insertion
Bram Moolenaar071d4272004-06-13 20:20:40 +00002827 line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low;
2828
2829 dp = (DATA_BL *)(hp->bh_data);
2830
2831/*
2832 * If
2833 * - there is not enough room in the current block
2834 * - appending to the last line in the block
2835 * - not appending to the last line in the file
2836 * insert in front of the next block.
2837 */
2838 if ((int)dp->db_free < space_needed && db_idx == line_count - 1
2839 && lnum < buf->b_ml.ml_line_count)
2840 {
2841 /*
2842 * Now that the line is not going to be inserted in the block that we
2843 * expected, the line count has to be adjusted in the pointer blocks
2844 * by using ml_locked_lineadd.
2845 */
2846 --(buf->b_ml.ml_locked_lineadd);
2847 --(buf->b_ml.ml_locked_high);
2848 if ((hp = ml_find_line(buf, lnum + 1, ML_INSERT)) == NULL)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002849 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002850
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002851 db_idx = -1; // careful, it is negative!
2852 // get line count before the insertion
Bram Moolenaar071d4272004-06-13 20:20:40 +00002853 line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low;
2854 CHECK(buf->b_ml.ml_locked_low != lnum + 1, "locked_low != lnum + 1");
2855
2856 dp = (DATA_BL *)(hp->bh_data);
2857 }
2858
2859 ++buf->b_ml.ml_line_count;
2860
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002861 if ((int)dp->db_free >= space_needed) // enough room in data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002862 {
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002863 /*
2864 * Insert the new line in an existing data block, or in the data block
2865 * allocated above.
2866 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002867 dp->db_txt_start -= len;
2868 dp->db_free -= space_needed;
2869 ++(dp->db_line_count);
2870
2871 /*
2872 * move the text of the lines that follow to the front
2873 * adjust the indexes of the lines that follow
2874 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002875 if (line_count > db_idx + 1) // if there are following lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00002876 {
2877 /*
2878 * Offset is the start of the previous line.
2879 * This will become the character just after the new line.
2880 */
2881 if (db_idx < 0)
2882 offset = dp->db_txt_end;
2883 else
2884 offset = ((dp->db_index[db_idx]) & DB_INDEX_MASK);
2885 mch_memmove((char *)dp + dp->db_txt_start,
2886 (char *)dp + dp->db_txt_start + len,
2887 (size_t)(offset - (dp->db_txt_start + len)));
2888 for (i = line_count - 1; i > db_idx; --i)
2889 dp->db_index[i + 1] = dp->db_index[i] - len;
2890 dp->db_index[db_idx + 1] = offset - len;
2891 }
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002892 else
2893 // add line at the end (which is the start of the text)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002894 dp->db_index[db_idx + 1] = dp->db_txt_start;
2895
2896 /*
2897 * copy the text into the block
2898 */
2899 mch_memmove((char *)dp + dp->db_index[db_idx + 1], line, (size_t)len);
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002900 if (flags & ML_APPEND_MARK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002901 dp->db_index[db_idx + 1] |= DB_MARKED;
2902
2903 /*
2904 * Mark the block dirty.
2905 */
2906 buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002907 if (!(flags & ML_APPEND_NEW))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002908 buf->b_ml.ml_flags |= ML_LOCKED_POS;
2909 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002910 else // not enough space in data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002911 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002912 long line_count_left, line_count_right;
2913 int page_count_left, page_count_right;
2914 bhdr_T *hp_left;
2915 bhdr_T *hp_right;
2916 bhdr_T *hp_new;
2917 int lines_moved;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002918 int data_moved = 0; // init to shut up gcc
2919 int total_moved = 0; // init to shut up gcc
Bram Moolenaar071d4272004-06-13 20:20:40 +00002920 DATA_BL *dp_right, *dp_left;
2921 int stack_idx;
2922 int in_left;
2923 int lineadd;
2924 blocknr_T bnum_left, bnum_right;
2925 linenr_T lnum_left, lnum_right;
2926 int pb_idx;
2927 PTR_BL *pp_new;
2928
2929 /*
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002930 * There is not enough room, we have to create a new data block and
2931 * copy some lines into it.
2932 * Then we have to insert an entry in the pointer block.
2933 * If this pointer block also is full, we go up another block, and so
2934 * on, up to the root if necessary.
2935 * The line counts in the pointer blocks have already been adjusted by
2936 * ml_find_line().
2937 *
Bram Moolenaar071d4272004-06-13 20:20:40 +00002938 * We are going to allocate a new data block. Depending on the
2939 * situation it will be put to the left or right of the existing
2940 * block. If possible we put the new line in the left block and move
2941 * the lines after it to the right block. Otherwise the new line is
2942 * also put in the right block. This method is more efficient when
2943 * inserting a lot of lines at one place.
2944 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002945 if (db_idx < 0) // left block is new, right block is existing
Bram Moolenaar071d4272004-06-13 20:20:40 +00002946 {
2947 lines_moved = 0;
2948 in_left = TRUE;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002949 // space_needed does not change
Bram Moolenaar071d4272004-06-13 20:20:40 +00002950 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002951 else // left block is existing, right block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00002952 {
2953 lines_moved = line_count - db_idx - 1;
2954 if (lines_moved == 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002955 in_left = FALSE; // put new line in right block
2956 // space_needed does not change
Bram Moolenaar071d4272004-06-13 20:20:40 +00002957 else
2958 {
2959 data_moved = ((dp->db_index[db_idx]) & DB_INDEX_MASK) -
2960 dp->db_txt_start;
2961 total_moved = data_moved + lines_moved * INDEX_SIZE;
2962 if ((int)dp->db_free + total_moved >= space_needed)
2963 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002964 in_left = TRUE; // put new line in left block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002965 space_needed = total_moved;
2966 }
2967 else
2968 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002969 in_left = FALSE; // put new line in right block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002970 space_needed += total_moved;
2971 }
2972 }
2973 }
2974
2975 page_count = ((space_needed + HEADER_SIZE) + page_size - 1) / page_size;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002976 if ((hp_new = ml_new_data(mfp, flags & ML_APPEND_NEW, page_count))
2977 == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002978 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002979 // correct line counts in pointer blocks
Bram Moolenaar071d4272004-06-13 20:20:40 +00002980 --(buf->b_ml.ml_locked_lineadd);
2981 --(buf->b_ml.ml_locked_high);
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002982 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002983 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002984 if (db_idx < 0) // left block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00002985 {
2986 hp_left = hp_new;
2987 hp_right = hp;
2988 line_count_left = 0;
2989 line_count_right = line_count;
2990 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002991 else // right block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00002992 {
2993 hp_left = hp;
2994 hp_right = hp_new;
2995 line_count_left = line_count;
2996 line_count_right = 0;
2997 }
2998 dp_right = (DATA_BL *)(hp_right->bh_data);
2999 dp_left = (DATA_BL *)(hp_left->bh_data);
3000 bnum_left = hp_left->bh_bnum;
3001 bnum_right = hp_right->bh_bnum;
3002 page_count_left = hp_left->bh_page_count;
3003 page_count_right = hp_right->bh_page_count;
3004
3005 /*
3006 * May move the new line into the right/new block.
3007 */
3008 if (!in_left)
3009 {
3010 dp_right->db_txt_start -= len;
3011 dp_right->db_free -= len + INDEX_SIZE;
3012 dp_right->db_index[0] = dp_right->db_txt_start;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003013 if (flags & ML_APPEND_MARK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003014 dp_right->db_index[0] |= DB_MARKED;
3015
3016 mch_memmove((char *)dp_right + dp_right->db_txt_start,
3017 line, (size_t)len);
3018 ++line_count_right;
3019 }
3020 /*
3021 * may move lines from the left/old block to the right/new one.
3022 */
3023 if (lines_moved)
3024 {
3025 /*
3026 */
3027 dp_right->db_txt_start -= data_moved;
3028 dp_right->db_free -= total_moved;
3029 mch_memmove((char *)dp_right + dp_right->db_txt_start,
3030 (char *)dp_left + dp_left->db_txt_start,
3031 (size_t)data_moved);
3032 offset = dp_right->db_txt_start - dp_left->db_txt_start;
3033 dp_left->db_txt_start += data_moved;
3034 dp_left->db_free += total_moved;
3035
3036 /*
3037 * update indexes in the new block
3038 */
3039 for (to = line_count_right, from = db_idx + 1;
3040 from < line_count_left; ++from, ++to)
3041 dp_right->db_index[to] = dp->db_index[from] + offset;
3042 line_count_right += lines_moved;
3043 line_count_left -= lines_moved;
3044 }
3045
3046 /*
3047 * May move the new line into the left (old or new) block.
3048 */
3049 if (in_left)
3050 {
3051 dp_left->db_txt_start -= len;
3052 dp_left->db_free -= len + INDEX_SIZE;
3053 dp_left->db_index[line_count_left] = dp_left->db_txt_start;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003054 if (flags & ML_APPEND_MARK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003055 dp_left->db_index[line_count_left] |= DB_MARKED;
3056 mch_memmove((char *)dp_left + dp_left->db_txt_start,
3057 line, (size_t)len);
3058 ++line_count_left;
3059 }
3060
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003061 if (db_idx < 0) // left block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00003062 {
3063 lnum_left = lnum + 1;
3064 lnum_right = 0;
3065 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003066 else // right block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00003067 {
3068 lnum_left = 0;
3069 if (in_left)
3070 lnum_right = lnum + 2;
3071 else
3072 lnum_right = lnum + 1;
3073 }
3074 dp_left->db_line_count = line_count_left;
3075 dp_right->db_line_count = line_count_right;
3076
3077 /*
3078 * release the two data blocks
3079 * The new one (hp_new) already has a correct blocknumber.
3080 * The old one (hp, in ml_locked) gets a positive blocknumber if
3081 * we changed it and we are not editing a new file.
3082 */
3083 if (lines_moved || in_left)
3084 buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003085 if (!(flags & ML_APPEND_NEW) && db_idx >= 0 && in_left)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003086 buf->b_ml.ml_flags |= ML_LOCKED_POS;
3087 mf_put(mfp, hp_new, TRUE, FALSE);
3088
3089 /*
3090 * flush the old data block
3091 * set ml_locked_lineadd to 0, because the updating of the
3092 * pointer blocks is done below
3093 */
3094 lineadd = buf->b_ml.ml_locked_lineadd;
3095 buf->b_ml.ml_locked_lineadd = 0;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003096 ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003097
3098 /*
3099 * update pointer blocks for the new data block
3100 */
3101 for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0;
3102 --stack_idx)
3103 {
3104 ip = &(buf->b_ml.ml_stack[stack_idx]);
3105 pb_idx = ip->ip_index;
3106 if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003107 goto theend;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003108 pp = (PTR_BL *)(hp->bh_data); // must be pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003109 if (pp->pb_id != PTR_ID)
3110 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003111 iemsg(_("E317: pointer block id wrong 3"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003112 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003113 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003114 }
3115 /*
3116 * TODO: If the pointer block is full and we are adding at the end
3117 * try to insert in front of the next block
3118 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003119 // block not full, add one entry
Bram Moolenaar071d4272004-06-13 20:20:40 +00003120 if (pp->pb_count < pp->pb_count_max)
3121 {
3122 if (pb_idx + 1 < (int)pp->pb_count)
3123 mch_memmove(&pp->pb_pointer[pb_idx + 2],
3124 &pp->pb_pointer[pb_idx + 1],
3125 (size_t)(pp->pb_count - pb_idx - 1) * sizeof(PTR_EN));
3126 ++pp->pb_count;
3127 pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
3128 pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
3129 pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
3130 pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right;
3131 pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right;
3132 pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right;
3133
3134 if (lnum_left != 0)
3135 pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left;
3136 if (lnum_right != 0)
3137 pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right;
3138
3139 mf_put(mfp, hp, TRUE, FALSE);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003140 buf->b_ml.ml_stack_top = stack_idx + 1; // truncate stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00003141
3142 if (lineadd)
3143 {
3144 --(buf->b_ml.ml_stack_top);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003145 // fix line count for rest of blocks in the stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00003146 ml_lineadd(buf, lineadd);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003147 // fix stack itself
Bram Moolenaar071d4272004-06-13 20:20:40 +00003148 buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high +=
3149 lineadd;
3150 ++(buf->b_ml.ml_stack_top);
3151 }
3152
3153 /*
3154 * We are finished, break the loop here.
3155 */
3156 break;
3157 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003158 else // pointer block full
Bram Moolenaar071d4272004-06-13 20:20:40 +00003159 {
3160 /*
3161 * split the pointer block
3162 * allocate a new pointer block
3163 * move some of the pointer into the new block
3164 * prepare for updating the parent block
3165 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003166 for (;;) // do this twice when splitting block 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00003167 {
3168 hp_new = ml_new_ptr(mfp);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003169 if (hp_new == NULL) // TODO: try to fix tree
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003170 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003171 pp_new = (PTR_BL *)(hp_new->bh_data);
3172
3173 if (hp->bh_bnum != 1)
3174 break;
3175
3176 /*
3177 * if block 1 becomes full the tree is given an extra level
3178 * The pointers from block 1 are moved into the new block.
3179 * block 1 is updated to point to the new block
3180 * then continue to split the new block
3181 */
3182 mch_memmove(pp_new, pp, (size_t)page_size);
3183 pp->pb_count = 1;
3184 pp->pb_pointer[0].pe_bnum = hp_new->bh_bnum;
3185 pp->pb_pointer[0].pe_line_count = buf->b_ml.ml_line_count;
3186 pp->pb_pointer[0].pe_old_lnum = 1;
3187 pp->pb_pointer[0].pe_page_count = 1;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003188 mf_put(mfp, hp, TRUE, FALSE); // release block 1
3189 hp = hp_new; // new block is to be split
Bram Moolenaar071d4272004-06-13 20:20:40 +00003190 pp = pp_new;
3191 CHECK(stack_idx != 0, _("stack_idx should be 0"));
3192 ip->ip_index = 0;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003193 ++stack_idx; // do block 1 again later
Bram Moolenaar071d4272004-06-13 20:20:40 +00003194 }
3195 /*
3196 * move the pointers after the current one to the new block
3197 * If there are none, the new entry will be in the new block.
3198 */
3199 total_moved = pp->pb_count - pb_idx - 1;
3200 if (total_moved)
3201 {
3202 mch_memmove(&pp_new->pb_pointer[0],
3203 &pp->pb_pointer[pb_idx + 1],
3204 (size_t)(total_moved) * sizeof(PTR_EN));
3205 pp_new->pb_count = total_moved;
3206 pp->pb_count -= total_moved - 1;
3207 pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right;
3208 pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right;
3209 pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right;
3210 if (lnum_right)
3211 pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right;
3212 }
3213 else
3214 {
3215 pp_new->pb_count = 1;
3216 pp_new->pb_pointer[0].pe_bnum = bnum_right;
3217 pp_new->pb_pointer[0].pe_line_count = line_count_right;
3218 pp_new->pb_pointer[0].pe_page_count = page_count_right;
3219 pp_new->pb_pointer[0].pe_old_lnum = lnum_right;
3220 }
3221 pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
3222 pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
3223 pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
3224 if (lnum_left)
3225 pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left;
3226 lnum_left = 0;
3227 lnum_right = 0;
3228
3229 /*
3230 * recompute line counts
3231 */
3232 line_count_right = 0;
3233 for (i = 0; i < (int)pp_new->pb_count; ++i)
3234 line_count_right += pp_new->pb_pointer[i].pe_line_count;
3235 line_count_left = 0;
3236 for (i = 0; i < (int)pp->pb_count; ++i)
3237 line_count_left += pp->pb_pointer[i].pe_line_count;
3238
3239 bnum_left = hp->bh_bnum;
3240 bnum_right = hp_new->bh_bnum;
3241 page_count_left = 1;
3242 page_count_right = 1;
3243 mf_put(mfp, hp, TRUE, FALSE);
3244 mf_put(mfp, hp_new, TRUE, FALSE);
3245 }
3246 }
3247
3248 /*
3249 * Safety check: fallen out of for loop?
3250 */
3251 if (stack_idx < 0)
3252 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003253 iemsg(_("E318: Updated too many blocks?"));
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003254 buf->b_ml.ml_stack_top = 0; // invalidate stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00003255 }
3256 }
3257
3258#ifdef FEAT_BYTEOFF
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003259 // The line was inserted below 'lnum'
Bram Moolenaar071d4272004-06-13 20:20:40 +00003260 ml_updatechunk(buf, lnum + 1, (long)len, ML_CHNK_ADDLINE);
3261#endif
3262#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02003263 if (netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +00003264 {
3265 if (STRLEN(line) > 0)
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003266 netbeans_inserted(buf, lnum+1, (colnr_T)0, line, (int)STRLEN(line));
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00003267 netbeans_inserted(buf, lnum+1, (colnr_T)STRLEN(line),
Bram Moolenaar071d4272004-06-13 20:20:40 +00003268 (char_u *)"\n", 1);
3269 }
3270#endif
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01003271#ifdef FEAT_JOB_CHANNEL
Bram Moolenaar99ef0622016-03-06 20:22:25 +01003272 if (buf->b_write_to_channel)
3273 channel_write_new_lines(buf);
3274#endif
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003275 ret = OK;
Bram Moolenaar99ef0622016-03-06 20:22:25 +01003276
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003277theend:
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003278#ifdef FEAT_PROP_POPUP
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003279 vim_free(tofree);
3280#endif
3281 return ret;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003282}
3283
3284/*
Bram Moolenaar250e3112019-07-15 23:02:14 +02003285 * Flush any pending change and call ml_append_int()
3286 */
3287 static int
3288ml_append_flush(
3289 buf_T *buf,
3290 linenr_T lnum, // append after this line (can be 0)
3291 char_u *line, // text of the new line
3292 colnr_T len, // length of line, including NUL, or 0
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003293 int flags) // ML_APPEND_ flags
Bram Moolenaar250e3112019-07-15 23:02:14 +02003294{
3295 if (lnum > buf->b_ml.ml_line_count)
3296 return FAIL; // lnum out of range
3297
3298 if (buf->b_ml.ml_line_lnum != 0)
3299 // This may also invoke ml_append_int().
3300 ml_flush_line(buf);
3301
3302#ifdef FEAT_EVAL
3303 // When inserting above recorded changes: flush the changes before changing
3304 // the text. Then flush the cached line, it may become invalid.
3305 may_invoke_listeners(buf, lnum + 1, lnum + 1, 1);
3306 if (buf->b_ml.ml_line_lnum != 0)
3307 ml_flush_line(buf);
3308#endif
3309
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003310 return ml_append_int(buf, lnum, line, len, flags);
Bram Moolenaar250e3112019-07-15 23:02:14 +02003311}
3312
3313/*
3314 * Append a line after lnum (may be 0 to insert a line in front of the file).
3315 * "line" does not need to be allocated, but can't be another line in a
3316 * buffer, unlocking may make it invalid.
3317 *
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003318 * "newfile": TRUE when starting to edit a new file, meaning that pe_old_lnum
Bram Moolenaar250e3112019-07-15 23:02:14 +02003319 * will be set for recovery
3320 * Check: The caller of this function should probably also call
3321 * appended_lines().
3322 *
3323 * return FAIL for failure, OK otherwise
3324 */
3325 int
3326ml_append(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003327 linenr_T lnum, // append after this line (can be 0)
3328 char_u *line, // text of the new line
3329 colnr_T len, // length of new line, including NUL, or 0
3330 int newfile) // flag, see above
Bram Moolenaar250e3112019-07-15 23:02:14 +02003331{
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003332 return ml_append_flags(lnum, line, len, newfile ? ML_APPEND_NEW : 0);
3333}
3334
3335 int
3336ml_append_flags(
3337 linenr_T lnum, // append after this line (can be 0)
3338 char_u *line, // text of the new line
3339 colnr_T len, // length of new line, including NUL, or 0
3340 int flags) // ML_APPEND_ values
3341{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003342 // When starting up, we might still need to create the memfile
Bram Moolenaar250e3112019-07-15 23:02:14 +02003343 if (curbuf->b_ml.ml_mfp == NULL && open_buffer(FALSE, NULL, 0) == FAIL)
3344 return FAIL;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003345 return ml_append_flush(curbuf, lnum, line, len, flags);
Bram Moolenaar250e3112019-07-15 23:02:14 +02003346}
3347
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003348
Bram Moolenaar250e3112019-07-15 23:02:14 +02003349#if defined(FEAT_SPELL) || defined(FEAT_QUICKFIX) || defined(PROTO)
3350/*
3351 * Like ml_append() but for an arbitrary buffer. The buffer must already have
3352 * a memline.
3353 */
3354 int
3355ml_append_buf(
3356 buf_T *buf,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003357 linenr_T lnum, // append after this line (can be 0)
3358 char_u *line, // text of the new line
3359 colnr_T len, // length of new line, including NUL, or 0
3360 int newfile) // flag, see above
Bram Moolenaar250e3112019-07-15 23:02:14 +02003361{
3362 if (buf->b_ml.ml_mfp == NULL)
3363 return FAIL;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003364 return ml_append_flush(buf, lnum, line, len, newfile ? ML_APPEND_NEW : 0);
Bram Moolenaar250e3112019-07-15 23:02:14 +02003365}
3366#endif
3367
3368/*
Bram Moolenaar5966ea12020-07-15 15:30:05 +02003369 * Replace line "lnum", with buffering, in current buffer.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003370 *
Bram Moolenaar1056d982006-03-09 22:37:52 +00003371 * If "copy" is TRUE, make a copy of the line, otherwise the line has been
Bram Moolenaar071d4272004-06-13 20:20:40 +00003372 * copied to allocated memory already.
Bram Moolenaar5966ea12020-07-15 15:30:05 +02003373 * If "copy" is FALSE the "line" may be freed to add text properties!
3374 * Do not use it after calling ml_replace().
Bram Moolenaar071d4272004-06-13 20:20:40 +00003375 *
3376 * Check: The caller of this function should probably also call
3377 * changed_lines(), unless update_screen(NOT_VALID) is used.
3378 *
3379 * return FAIL for failure, OK otherwise
3380 */
3381 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003382ml_replace(linenr_T lnum, char_u *line, int copy)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003383{
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003384 colnr_T len = -1;
3385
3386 if (line != NULL)
Bram Moolenaar4efe73b2018-12-16 14:37:39 +01003387 len = (colnr_T)STRLEN(line);
Bram Moolenaarccae4672019-01-04 15:09:57 +01003388 return ml_replace_len(lnum, line, len, FALSE, copy);
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003389}
3390
Bram Moolenaarccae4672019-01-04 15:09:57 +01003391/*
3392 * Replace a line for the current buffer. Like ml_replace() with:
3393 * "len_arg" is the length of the text, excluding NUL.
3394 * If "has_props" is TRUE then "line_arg" includes the text properties and
3395 * "len_arg" includes the NUL of the text.
3396 */
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003397 int
Bram Moolenaarccae4672019-01-04 15:09:57 +01003398ml_replace_len(
3399 linenr_T lnum,
3400 char_u *line_arg,
3401 colnr_T len_arg,
3402 int has_props,
3403 int copy)
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003404{
3405 char_u *line = line_arg;
3406 colnr_T len = len_arg;
3407
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003408 if (line == NULL) // just checking...
Bram Moolenaar071d4272004-06-13 20:20:40 +00003409 return FAIL;
3410
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003411 // When starting up, we might still need to create the memfile
Bram Moolenaar59f931e2010-07-24 20:27:03 +02003412 if (curbuf->b_ml.ml_mfp == NULL && open_buffer(FALSE, NULL, 0) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003413 return FAIL;
3414
Bram Moolenaarccae4672019-01-04 15:09:57 +01003415 if (!has_props)
3416 ++len; // include the NUL after the text
3417 if (copy)
3418 {
3419 // copy the line to allocated memory
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003420#ifdef FEAT_PROP_POPUP
Bram Moolenaarccae4672019-01-04 15:09:57 +01003421 if (has_props)
3422 line = vim_memsave(line, len);
3423 else
3424#endif
3425 line = vim_strnsave(line, len - 1);
3426 if (line == NULL)
3427 return FAIL;
3428 }
3429
Bram Moolenaar071d4272004-06-13 20:20:40 +00003430#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02003431 if (netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +00003432 {
3433 netbeans_removed(curbuf, lnum, 0, (long)STRLEN(ml_get(lnum)));
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003434 netbeans_inserted(curbuf, lnum, 0, line, (int)STRLEN(line));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003435 }
3436#endif
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003437 if (curbuf->b_ml.ml_line_lnum != lnum)
3438 {
3439 // another line is buffered, flush it
3440 ml_flush_line(curbuf);
Bram Moolenaarca79a5f2018-12-13 23:16:36 +01003441 curbuf->b_ml.ml_flags &= ~ML_LINE_DIRTY;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003442
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003443#ifdef FEAT_PROP_POPUP
Bram Moolenaarccae4672019-01-04 15:09:57 +01003444 if (curbuf->b_has_textprop && !has_props)
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003445 // Need to fetch the old line to copy over any text properties.
3446 ml_get_buf(curbuf, lnum, TRUE);
3447#endif
3448 }
3449
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003450#ifdef FEAT_PROP_POPUP
Bram Moolenaarccae4672019-01-04 15:09:57 +01003451 if (curbuf->b_has_textprop && !has_props)
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003452 {
3453 size_t oldtextlen = STRLEN(curbuf->b_ml.ml_line_ptr) + 1;
3454
3455 if (oldtextlen < (size_t)curbuf->b_ml.ml_line_len)
3456 {
3457 char_u *newline;
3458 size_t textproplen = curbuf->b_ml.ml_line_len - oldtextlen;
3459
3460 // Need to copy over text properties, stored after the text.
Bram Moolenaarccae4672019-01-04 15:09:57 +01003461 newline = alloc(len + (int)textproplen);
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003462 if (newline != NULL)
3463 {
Bram Moolenaarccae4672019-01-04 15:09:57 +01003464 mch_memmove(newline, line, len);
Bram Moolenaar0d3cb732019-05-18 13:05:18 +02003465 mch_memmove(newline + len, curbuf->b_ml.ml_line_ptr
3466 + oldtextlen, textproplen);
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003467 vim_free(line);
3468 line = newline;
Bram Moolenaar4efe73b2018-12-16 14:37:39 +01003469 len += (colnr_T)textproplen;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003470 }
3471 }
3472 }
3473#endif
3474
Bram Moolenaarccae4672019-01-04 15:09:57 +01003475 if (curbuf->b_ml.ml_flags & ML_LINE_DIRTY) // same line allocated
3476 vim_free(curbuf->b_ml.ml_line_ptr); // free it
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003477
Bram Moolenaar071d4272004-06-13 20:20:40 +00003478 curbuf->b_ml.ml_line_ptr = line;
Bram Moolenaarccae4672019-01-04 15:09:57 +01003479 curbuf->b_ml.ml_line_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003480 curbuf->b_ml.ml_line_lnum = lnum;
3481 curbuf->b_ml.ml_flags = (curbuf->b_ml.ml_flags | ML_LINE_DIRTY) & ~ML_EMPTY;
3482
3483 return OK;
3484}
3485
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003486#ifdef FEAT_PROP_POPUP
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003487/*
3488 * Adjust text properties in line "lnum" for a deleted line.
3489 * When "above" is true this is the line above the deleted line.
3490 * "del_props" are the properties of the deleted line.
3491 */
3492 static void
3493adjust_text_props_for_delete(
3494 buf_T *buf,
3495 linenr_T lnum,
3496 char_u *del_props,
3497 int del_props_len,
3498 int above)
3499{
3500 int did_get_line = FALSE;
3501 int done_del;
3502 int done_this;
3503 textprop_T prop_del;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003504 bhdr_T *hp;
3505 DATA_BL *dp;
3506 int idx;
3507 int line_start;
3508 long line_size;
3509 int this_props_len;
3510 char_u *text;
3511 size_t textlen;
3512 int found;
3513
3514 for (done_del = 0; done_del < del_props_len; done_del += sizeof(textprop_T))
3515 {
3516 mch_memmove(&prop_del, del_props + done_del, sizeof(textprop_T));
3517 if ((above && (prop_del.tp_flags & TP_FLAG_CONT_PREV)
3518 && !(prop_del.tp_flags & TP_FLAG_CONT_NEXT))
3519 || (!above && (prop_del.tp_flags & TP_FLAG_CONT_NEXT)
3520 && !(prop_del.tp_flags & TP_FLAG_CONT_PREV)))
3521 {
3522 if (!did_get_line)
3523 {
3524 did_get_line = TRUE;
3525 if ((hp = ml_find_line(buf, lnum, ML_FIND)) == NULL)
3526 return;
3527
3528 dp = (DATA_BL *)(hp->bh_data);
3529 idx = lnum - buf->b_ml.ml_locked_low;
3530 line_start = ((dp->db_index[idx]) & DB_INDEX_MASK);
3531 if (idx == 0) // first line in block, text at the end
3532 line_size = dp->db_txt_end - line_start;
3533 else
Bram Moolenaar87be9be2020-05-30 15:32:02 +02003534 line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK)
3535 - line_start;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003536 text = (char_u *)dp + line_start;
3537 textlen = STRLEN(text) + 1;
3538 if ((long)textlen >= line_size)
3539 {
3540 if (above)
3541 internal_error("no text property above deleted line");
3542 else
3543 internal_error("no text property below deleted line");
3544 return;
3545 }
Bram Moolenaar4b7214e2019-01-03 21:55:32 +01003546 this_props_len = line_size - (int)textlen;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003547 }
3548
3549 found = FALSE;
Bram Moolenaar87be9be2020-05-30 15:32:02 +02003550 for (done_this = 0; done_this < this_props_len;
3551 done_this += sizeof(textprop_T))
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003552 {
Bram Moolenaar87be9be2020-05-30 15:32:02 +02003553 int flag = above ? TP_FLAG_CONT_NEXT
3554 : TP_FLAG_CONT_PREV;
3555 textprop_T prop_this;
3556
3557 mch_memmove(&prop_this, text + textlen + done_del,
3558 sizeof(textprop_T));
3559 if ((prop_this.tp_flags & flag)
3560 && prop_del.tp_id == prop_this.tp_id
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003561 && prop_del.tp_type == prop_this.tp_type)
3562 {
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003563 found = TRUE;
Bram Moolenaar87be9be2020-05-30 15:32:02 +02003564 prop_this.tp_flags &= ~flag;
3565 mch_memmove(text + textlen + done_del, &prop_this,
3566 sizeof(textprop_T));
3567 break;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003568 }
3569 }
3570 if (!found)
3571 {
3572 if (above)
3573 internal_error("text property above deleted line not found");
3574 else
3575 internal_error("text property below deleted line not found");
3576 }
3577
3578 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
3579 }
3580 }
3581}
3582#endif
3583
Bram Moolenaar071d4272004-06-13 20:20:40 +00003584/*
Bram Moolenaar4033c552017-09-16 20:54:51 +02003585 * Delete line "lnum" in the current buffer.
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003586 * When "flags" has ML_DEL_MESSAGE may give a "No lines in buffer" message.
3587 * When "flags" has ML_DEL_UNDO this is called from undo.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003588 *
3589 * return FAIL for failure, OK otherwise
3590 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003591 static int
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003592ml_delete_int(buf_T *buf, linenr_T lnum, int flags)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003593{
3594 bhdr_T *hp;
3595 memfile_T *mfp;
3596 DATA_BL *dp;
3597 PTR_BL *pp;
3598 infoptr_T *ip;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003599 int count; // number of entries in block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003600 int idx;
3601 int stack_idx;
3602 int text_start;
3603 int line_start;
3604 long line_size;
3605 int i;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003606 int ret = FAIL;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003607#ifdef FEAT_PROP_POPUP
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003608 char_u *textprop_save = NULL;
3609 int textprop_save_len;
3610#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003611
Bram Moolenaar071d4272004-06-13 20:20:40 +00003612 if (lowest_marked && lowest_marked > lnum)
3613 lowest_marked--;
3614
3615/*
3616 * If the file becomes empty the last line is replaced by an empty line.
3617 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003618 if (buf->b_ml.ml_line_count == 1) // file becomes empty
Bram Moolenaar071d4272004-06-13 20:20:40 +00003619 {
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003620 if ((flags & ML_DEL_MESSAGE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003621#ifdef FEAT_NETBEANS_INTG
3622 && !netbeansSuppressNoLines
3623#endif
3624 )
Bram Moolenaar238a5642006-02-21 22:12:05 +00003625 set_keep_msg((char_u *)_(no_lines_msg), 0);
3626
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003627 // FEAT_BYTEOFF already handled in there, don't worry 'bout it below
Bram Moolenaar071d4272004-06-13 20:20:40 +00003628 i = ml_replace((linenr_T)1, (char_u *)"", TRUE);
3629 buf->b_ml.ml_flags |= ML_EMPTY;
3630
3631 return i;
3632 }
3633
3634/*
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003635 * Find the data block containing the line.
3636 * This also fills the stack with the blocks from the root to the data block.
3637 * This also releases any locked block..
Bram Moolenaar071d4272004-06-13 20:20:40 +00003638 */
3639 mfp = buf->b_ml.ml_mfp;
3640 if (mfp == NULL)
3641 return FAIL;
3642
3643 if ((hp = ml_find_line(buf, lnum, ML_DELETE)) == NULL)
3644 return FAIL;
3645
3646 dp = (DATA_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003647 // compute line count before the delete
Bram Moolenaar071d4272004-06-13 20:20:40 +00003648 count = (long)(buf->b_ml.ml_locked_high)
3649 - (long)(buf->b_ml.ml_locked_low) + 2;
3650 idx = lnum - buf->b_ml.ml_locked_low;
3651
3652 --buf->b_ml.ml_line_count;
3653
3654 line_start = ((dp->db_index[idx]) & DB_INDEX_MASK);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003655 if (idx == 0) // first line in block, text at the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00003656 line_size = dp->db_txt_end - line_start;
3657 else
3658 line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK) - line_start;
3659
3660#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02003661 if (netbeans_active())
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00003662 netbeans_removed(buf, lnum, 0, (long)line_size);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003663#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003664#ifdef FEAT_PROP_POPUP
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003665 // If there are text properties, make a copy, so that we can update
3666 // properties in preceding and following lines.
Bram Moolenaar4cd5c522021-06-27 13:04:00 +02003667 if (buf->b_has_textprop && !(flags & (ML_DEL_UNDO | ML_DEL_NOPROP)))
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003668 {
3669 size_t textlen = STRLEN((char_u *)dp + line_start) + 1;
3670
3671 if ((long)textlen < line_size)
3672 {
Bram Moolenaar4b7214e2019-01-03 21:55:32 +01003673 textprop_save_len = line_size - (int)textlen;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003674 textprop_save = vim_memsave((char_u *)dp + line_start + textlen,
3675 textprop_save_len);
3676 }
3677 }
3678#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003679
3680/*
3681 * special case: If there is only one line in the data block it becomes empty.
3682 * Then we have to remove the entry, pointing to this data block, from the
3683 * pointer block. If this pointer block also becomes empty, we go up another
3684 * block, and so on, up to the root if necessary.
3685 * The line counts in the pointer blocks have already been adjusted by
3686 * ml_find_line().
3687 */
3688 if (count == 1)
3689 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003690 mf_free(mfp, hp); // free the data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003691 buf->b_ml.ml_locked = NULL;
3692
Bram Moolenaare60acc12011-05-10 16:41:25 +02003693 for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0;
3694 --stack_idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003695 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003696 buf->b_ml.ml_stack_top = 0; // stack is invalid when failing
Bram Moolenaar071d4272004-06-13 20:20:40 +00003697 ip = &(buf->b_ml.ml_stack[stack_idx]);
3698 idx = ip->ip_index;
3699 if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003700 goto theend;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003701 pp = (PTR_BL *)(hp->bh_data); // must be pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003702 if (pp->pb_id != PTR_ID)
3703 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003704 iemsg(_("E317: pointer block id wrong 4"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003705 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003706 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003707 }
3708 count = --(pp->pb_count);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003709 if (count == 0) // the pointer block becomes empty!
Bram Moolenaar071d4272004-06-13 20:20:40 +00003710 mf_free(mfp, hp);
3711 else
3712 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003713 if (count != idx) // move entries after the deleted one
Bram Moolenaar071d4272004-06-13 20:20:40 +00003714 mch_memmove(&pp->pb_pointer[idx], &pp->pb_pointer[idx + 1],
3715 (size_t)(count - idx) * sizeof(PTR_EN));
3716 mf_put(mfp, hp, TRUE, FALSE);
3717
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003718 buf->b_ml.ml_stack_top = stack_idx; // truncate stack
3719 // fix line count for rest of blocks in the stack
Bram Moolenaar6b803a72007-05-06 14:25:46 +00003720 if (buf->b_ml.ml_locked_lineadd != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003721 {
3722 ml_lineadd(buf, buf->b_ml.ml_locked_lineadd);
3723 buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high +=
Bram Moolenaar6b803a72007-05-06 14:25:46 +00003724 buf->b_ml.ml_locked_lineadd;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003725 }
3726 ++(buf->b_ml.ml_stack_top);
3727
3728 break;
3729 }
3730 }
3731 CHECK(stack_idx < 0, _("deleted block 1?"));
3732 }
3733 else
3734 {
3735 /*
3736 * delete the text by moving the next lines forwards
3737 */
3738 text_start = dp->db_txt_start;
3739 mch_memmove((char *)dp + text_start + line_size,
3740 (char *)dp + text_start, (size_t)(line_start - text_start));
3741
3742 /*
3743 * delete the index by moving the next indexes backwards
3744 * Adjust the indexes for the text movement.
3745 */
3746 for (i = idx; i < count - 1; ++i)
3747 dp->db_index[i] = dp->db_index[i + 1] + line_size;
3748
3749 dp->db_free += line_size + INDEX_SIZE;
3750 dp->db_txt_start += line_size;
3751 --(dp->db_line_count);
3752
3753 /*
3754 * mark the block dirty and make sure it is in the file (for recovery)
3755 */
3756 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
3757 }
3758
3759#ifdef FEAT_BYTEOFF
3760 ml_updatechunk(buf, lnum, line_size, ML_CHNK_DELLINE);
3761#endif
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003762 ret = OK;
3763
3764theend:
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003765#ifdef FEAT_PROP_POPUP
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003766 if (textprop_save != NULL)
3767 {
3768 // Adjust text properties in the line above and below.
3769 if (lnum > 1)
Bram Moolenaar4cd5c522021-06-27 13:04:00 +02003770 adjust_text_props_for_delete(buf, lnum - 1, textprop_save,
3771 textprop_save_len, TRUE);
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003772 if (lnum <= buf->b_ml.ml_line_count)
Bram Moolenaar4cd5c522021-06-27 13:04:00 +02003773 adjust_text_props_for_delete(buf, lnum, textprop_save,
3774 textprop_save_len, FALSE);
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003775 }
3776 vim_free(textprop_save);
3777#endif
3778 return ret;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003779}
3780
3781/*
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003782 * Delete line "lnum" in the current buffer.
3783 * When "message" is TRUE may give a "No lines in buffer" message.
3784 *
3785 * Check: The caller of this function should probably also call
3786 * deleted_lines() after this.
3787 *
3788 * return FAIL for failure, OK otherwise
3789 */
3790 int
Bram Moolenaarca70c072020-05-30 20:30:46 +02003791ml_delete(linenr_T lnum)
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003792{
Bram Moolenaarca70c072020-05-30 20:30:46 +02003793 return ml_delete_flags(lnum, 0);
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003794}
3795
3796/*
3797 * Like ml_delete() but using flags (see ml_delete_int()).
3798 */
3799 int
3800ml_delete_flags(linenr_T lnum, int flags)
3801{
3802 ml_flush_line(curbuf);
3803 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count)
3804 return FAIL;
3805
3806#ifdef FEAT_EVAL
3807 // When inserting above recorded changes: flush the changes before changing
3808 // the text.
3809 may_invoke_listeners(curbuf, lnum, lnum + 1, -1);
3810#endif
3811
3812 return ml_delete_int(curbuf, lnum, flags);
3813}
3814
3815/*
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003816 * set the DB_MARKED flag for line 'lnum'
Bram Moolenaar071d4272004-06-13 20:20:40 +00003817 */
3818 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003819ml_setmarked(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003820{
3821 bhdr_T *hp;
3822 DATA_BL *dp;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003823 // invalid line number
Bram Moolenaar071d4272004-06-13 20:20:40 +00003824 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count
3825 || curbuf->b_ml.ml_mfp == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003826 return; // give error message?
Bram Moolenaar071d4272004-06-13 20:20:40 +00003827
3828 if (lowest_marked == 0 || lowest_marked > lnum)
3829 lowest_marked = lnum;
3830
3831 /*
3832 * find the data block containing the line
3833 * This also fills the stack with the blocks from the root to the data block
3834 * This also releases any locked block.
3835 */
3836 if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003837 return; // give error message?
Bram Moolenaar071d4272004-06-13 20:20:40 +00003838
3839 dp = (DATA_BL *)(hp->bh_data);
3840 dp->db_index[lnum - curbuf->b_ml.ml_locked_low] |= DB_MARKED;
3841 curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
3842}
3843
3844/*
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003845 * find the first line with its DB_MARKED flag set
Bram Moolenaar071d4272004-06-13 20:20:40 +00003846 */
3847 linenr_T
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003848ml_firstmarked(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003849{
3850 bhdr_T *hp;
3851 DATA_BL *dp;
3852 linenr_T lnum;
3853 int i;
3854
3855 if (curbuf->b_ml.ml_mfp == NULL)
3856 return (linenr_T) 0;
3857
3858 /*
3859 * The search starts with lowest_marked line. This is the last line where
3860 * a mark was found, adjusted by inserting/deleting lines.
3861 */
3862 for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count; )
3863 {
3864 /*
3865 * Find the data block containing the line.
3866 * This also fills the stack with the blocks from the root to the data
3867 * block This also releases any locked block.
3868 */
3869 if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003870 return (linenr_T)0; // give error message?
Bram Moolenaar071d4272004-06-13 20:20:40 +00003871
3872 dp = (DATA_BL *)(hp->bh_data);
3873
3874 for (i = lnum - curbuf->b_ml.ml_locked_low;
3875 lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum)
3876 if ((dp->db_index[i]) & DB_MARKED)
3877 {
3878 (dp->db_index[i]) &= DB_INDEX_MASK;
3879 curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
3880 lowest_marked = lnum + 1;
3881 return lnum;
3882 }
3883 }
3884
3885 return (linenr_T) 0;
3886}
3887
Bram Moolenaar071d4272004-06-13 20:20:40 +00003888/*
3889 * clear all DB_MARKED flags
3890 */
3891 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003892ml_clearmarked(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003893{
3894 bhdr_T *hp;
3895 DATA_BL *dp;
3896 linenr_T lnum;
3897 int i;
3898
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003899 if (curbuf->b_ml.ml_mfp == NULL) // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00003900 return;
3901
3902 /*
3903 * The search starts with line lowest_marked.
3904 */
3905 for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count; )
3906 {
3907 /*
3908 * Find the data block containing the line.
3909 * This also fills the stack with the blocks from the root to the data
3910 * block and releases any locked block.
3911 */
3912 if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003913 return; // give error message?
Bram Moolenaar071d4272004-06-13 20:20:40 +00003914
3915 dp = (DATA_BL *)(hp->bh_data);
3916
3917 for (i = lnum - curbuf->b_ml.ml_locked_low;
3918 lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum)
3919 if ((dp->db_index[i]) & DB_MARKED)
3920 {
3921 (dp->db_index[i]) &= DB_INDEX_MASK;
3922 curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
3923 }
3924 }
3925
3926 lowest_marked = 0;
3927 return;
3928}
3929
3930/*
3931 * flush ml_line if necessary
3932 */
3933 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003934ml_flush_line(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003935{
3936 bhdr_T *hp;
3937 DATA_BL *dp;
3938 linenr_T lnum;
3939 char_u *new_line;
3940 char_u *old_line;
3941 colnr_T new_len;
3942 int old_len;
3943 int extra;
3944 int idx;
3945 int start;
3946 int count;
3947 int i;
Bram Moolenaar0ca4b352010-02-11 18:54:43 +01003948 static int entered = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003949
3950 if (buf->b_ml.ml_line_lnum == 0 || buf->b_ml.ml_mfp == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003951 return; // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00003952
3953 if (buf->b_ml.ml_flags & ML_LINE_DIRTY)
3954 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003955 // This code doesn't work recursively, but Netbeans may call back here
3956 // when obtaining the cursor position.
Bram Moolenaar0ca4b352010-02-11 18:54:43 +01003957 if (entered)
3958 return;
3959 entered = TRUE;
3960
Bram Moolenaar071d4272004-06-13 20:20:40 +00003961 lnum = buf->b_ml.ml_line_lnum;
3962 new_line = buf->b_ml.ml_line_ptr;
3963
3964 hp = ml_find_line(buf, lnum, ML_FIND);
3965 if (hp == NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003966 siemsg(_("E320: Cannot find line %ld"), lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003967 else
3968 {
3969 dp = (DATA_BL *)(hp->bh_data);
3970 idx = lnum - buf->b_ml.ml_locked_low;
3971 start = ((dp->db_index[idx]) & DB_INDEX_MASK);
3972 old_line = (char_u *)dp + start;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003973 if (idx == 0) // line is last in block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003974 old_len = dp->db_txt_end - start;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003975 else // text of previous line follows
Bram Moolenaar071d4272004-06-13 20:20:40 +00003976 old_len = (dp->db_index[idx - 1] & DB_INDEX_MASK) - start;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003977 new_len = buf->b_ml.ml_line_len;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003978 extra = new_len - old_len; // negative if lines gets smaller
Bram Moolenaar071d4272004-06-13 20:20:40 +00003979
3980 /*
3981 * if new line fits in data block, replace directly
3982 */
3983 if ((int)dp->db_free >= extra)
3984 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003985 // if the length changes and there are following lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00003986 count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 1;
3987 if (extra != 0 && idx < count - 1)
3988 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003989 // move text of following lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00003990 mch_memmove((char *)dp + dp->db_txt_start - extra,
3991 (char *)dp + dp->db_txt_start,
3992 (size_t)(start - dp->db_txt_start));
3993
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003994 // adjust pointers of this and following lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00003995 for (i = idx + 1; i < count; ++i)
3996 dp->db_index[i] -= extra;
3997 }
3998 dp->db_index[idx] -= extra;
3999
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004000 // adjust free space
Bram Moolenaar071d4272004-06-13 20:20:40 +00004001 dp->db_free -= extra;
4002 dp->db_txt_start -= extra;
4003
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004004 // copy new line into the data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00004005 mch_memmove(old_line - extra, new_line, (size_t)new_len);
4006 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
4007#ifdef FEAT_BYTEOFF
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004008 // The else case is already covered by the insert and delete
Bram Moolenaar071d4272004-06-13 20:20:40 +00004009 ml_updatechunk(buf, lnum, (long)extra, ML_CHNK_UPDLINE);
4010#endif
4011 }
4012 else
4013 {
4014 /*
4015 * Cannot do it in one data block: Delete and append.
4016 * Append first, because ml_delete_int() cannot delete the
4017 * last line in a buffer, which causes trouble for a buffer
4018 * that has only one line.
4019 * Don't forget to copy the mark!
4020 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004021 // How about handling errors???
Bram Moolenaara9d4b842020-05-30 14:46:52 +02004022 (void)ml_append_int(buf, lnum, new_line, new_len,
Bram Moolenaar840f91f2021-05-26 22:32:10 +02004023 ((dp->db_index[idx] & DB_MARKED) ? ML_APPEND_MARK : 0)
4024#ifdef FEAT_PROP_POPUP
4025 | ML_APPEND_NOPROP
4026#endif
4027 );
Bram Moolenaar4cd5c522021-06-27 13:04:00 +02004028 (void)ml_delete_int(buf, lnum, ML_DEL_NOPROP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004029 }
4030 }
4031 vim_free(new_line);
Bram Moolenaar0ca4b352010-02-11 18:54:43 +01004032
4033 entered = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004034 }
4035
4036 buf->b_ml.ml_line_lnum = 0;
4037}
4038
4039/*
4040 * create a new, empty, data block
4041 */
4042 static bhdr_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004043ml_new_data(memfile_T *mfp, int negative, int page_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004044{
4045 bhdr_T *hp;
4046 DATA_BL *dp;
4047
4048 if ((hp = mf_new(mfp, negative, page_count)) == NULL)
4049 return NULL;
4050
4051 dp = (DATA_BL *)(hp->bh_data);
4052 dp->db_id = DATA_ID;
4053 dp->db_txt_start = dp->db_txt_end = page_count * mfp->mf_page_size;
4054 dp->db_free = dp->db_txt_start - HEADER_SIZE;
4055 dp->db_line_count = 0;
4056
4057 return hp;
4058}
4059
4060/*
4061 * create a new, empty, pointer block
4062 */
4063 static bhdr_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004064ml_new_ptr(memfile_T *mfp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004065{
4066 bhdr_T *hp;
4067 PTR_BL *pp;
4068
4069 if ((hp = mf_new(mfp, FALSE, 1)) == NULL)
4070 return NULL;
4071
4072 pp = (PTR_BL *)(hp->bh_data);
4073 pp->pb_id = PTR_ID;
4074 pp->pb_count = 0;
Bram Moolenaar20a825a2010-05-31 21:27:30 +02004075 pp->pb_count_max = (short_u)((mfp->mf_page_size - sizeof(PTR_BL))
4076 / sizeof(PTR_EN) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004077
4078 return hp;
4079}
4080
4081/*
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01004082 * Lookup line 'lnum' in a memline.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004083 *
4084 * action: if ML_DELETE or ML_INSERT the line count is updated while searching
4085 * if ML_FLUSH only flush a locked block
4086 * if ML_FIND just find the line
4087 *
4088 * If the block was found it is locked and put in ml_locked.
4089 * The stack is updated to lead to the locked block. The ip_high field in
4090 * the stack is updated to reflect the last line in the block AFTER the
4091 * insert or delete, also if the pointer block has not been updated yet. But
Bram Moolenaar6b803a72007-05-06 14:25:46 +00004092 * if ml_locked != NULL ml_locked_lineadd must be added to ip_high.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004093 *
4094 * return: NULL for failure, pointer to block header otherwise
4095 */
4096 static bhdr_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004097ml_find_line(buf_T *buf, linenr_T lnum, int action)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004098{
4099 DATA_BL *dp;
4100 PTR_BL *pp;
4101 infoptr_T *ip;
4102 bhdr_T *hp;
4103 memfile_T *mfp;
4104 linenr_T t;
4105 blocknr_T bnum, bnum2;
4106 int dirty;
4107 linenr_T low, high;
4108 int top;
4109 int page_count;
4110 int idx;
4111
4112 mfp = buf->b_ml.ml_mfp;
4113
4114 /*
4115 * If there is a locked block check if the wanted line is in it.
4116 * If not, flush and release the locked block.
4117 * Don't do this for ML_INSERT_SAME, because the stack need to be updated.
4118 * Don't do this for ML_FLUSH, because we want to flush the locked block.
Bram Moolenaar47b8b152007-02-07 02:41:57 +00004119 * Don't do this when 'swapfile' is reset, we want to load all the blocks.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004120 */
4121 if (buf->b_ml.ml_locked)
4122 {
Bram Moolenaar47b8b152007-02-07 02:41:57 +00004123 if (ML_SIMPLE(action)
4124 && buf->b_ml.ml_locked_low <= lnum
4125 && buf->b_ml.ml_locked_high >= lnum
4126 && !mf_dont_release)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004127 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004128 // remember to update pointer blocks and stack later
Bram Moolenaar071d4272004-06-13 20:20:40 +00004129 if (action == ML_INSERT)
4130 {
4131 ++(buf->b_ml.ml_locked_lineadd);
4132 ++(buf->b_ml.ml_locked_high);
4133 }
4134 else if (action == ML_DELETE)
4135 {
4136 --(buf->b_ml.ml_locked_lineadd);
4137 --(buf->b_ml.ml_locked_high);
4138 }
4139 return (buf->b_ml.ml_locked);
4140 }
4141
4142 mf_put(mfp, buf->b_ml.ml_locked, buf->b_ml.ml_flags & ML_LOCKED_DIRTY,
4143 buf->b_ml.ml_flags & ML_LOCKED_POS);
4144 buf->b_ml.ml_locked = NULL;
4145
Bram Moolenaar6b803a72007-05-06 14:25:46 +00004146 /*
4147 * If lines have been added or deleted in the locked block, need to
4148 * update the line count in pointer blocks.
4149 */
4150 if (buf->b_ml.ml_locked_lineadd != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004151 ml_lineadd(buf, buf->b_ml.ml_locked_lineadd);
4152 }
4153
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004154 if (action == ML_FLUSH) // nothing else to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00004155 return NULL;
4156
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004157 bnum = 1; // start at the root of the tree
Bram Moolenaar071d4272004-06-13 20:20:40 +00004158 page_count = 1;
4159 low = 1;
4160 high = buf->b_ml.ml_line_count;
4161
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004162 if (action == ML_FIND) // first try stack entries
Bram Moolenaar071d4272004-06-13 20:20:40 +00004163 {
4164 for (top = buf->b_ml.ml_stack_top - 1; top >= 0; --top)
4165 {
4166 ip = &(buf->b_ml.ml_stack[top]);
4167 if (ip->ip_low <= lnum && ip->ip_high >= lnum)
4168 {
4169 bnum = ip->ip_bnum;
4170 low = ip->ip_low;
4171 high = ip->ip_high;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004172 buf->b_ml.ml_stack_top = top; // truncate stack at prev entry
Bram Moolenaar071d4272004-06-13 20:20:40 +00004173 break;
4174 }
4175 }
4176 if (top < 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004177 buf->b_ml.ml_stack_top = 0; // not found, start at the root
Bram Moolenaar071d4272004-06-13 20:20:40 +00004178 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004179 else // ML_DELETE or ML_INSERT
4180 buf->b_ml.ml_stack_top = 0; // start at the root
Bram Moolenaar071d4272004-06-13 20:20:40 +00004181
4182/*
4183 * search downwards in the tree until a data block is found
4184 */
4185 for (;;)
4186 {
4187 if ((hp = mf_get(mfp, bnum, page_count)) == NULL)
4188 goto error_noblock;
4189
4190 /*
4191 * update high for insert/delete
4192 */
4193 if (action == ML_INSERT)
4194 ++high;
4195 else if (action == ML_DELETE)
4196 --high;
4197
4198 dp = (DATA_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004199 if (dp->db_id == DATA_ID) // data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00004200 {
4201 buf->b_ml.ml_locked = hp;
4202 buf->b_ml.ml_locked_low = low;
4203 buf->b_ml.ml_locked_high = high;
4204 buf->b_ml.ml_locked_lineadd = 0;
4205 buf->b_ml.ml_flags &= ~(ML_LOCKED_DIRTY | ML_LOCKED_POS);
4206 return hp;
4207 }
4208
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004209 pp = (PTR_BL *)(dp); // must be pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00004210 if (pp->pb_id != PTR_ID)
4211 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004212 iemsg(_("E317: pointer block id wrong"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004213 goto error_block;
4214 }
4215
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004216 if ((top = ml_add_stack(buf)) < 0) // add new entry to stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00004217 goto error_block;
4218 ip = &(buf->b_ml.ml_stack[top]);
4219 ip->ip_bnum = bnum;
4220 ip->ip_low = low;
4221 ip->ip_high = high;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004222 ip->ip_index = -1; // index not known yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00004223
4224 dirty = FALSE;
4225 for (idx = 0; idx < (int)pp->pb_count; ++idx)
4226 {
4227 t = pp->pb_pointer[idx].pe_line_count;
4228 CHECK(t == 0, _("pe_line_count is zero"));
4229 if ((low += t) > lnum)
4230 {
4231 ip->ip_index = idx;
4232 bnum = pp->pb_pointer[idx].pe_bnum;
4233 page_count = pp->pb_pointer[idx].pe_page_count;
4234 high = low - 1;
4235 low -= t;
4236
4237 /*
4238 * a negative block number may have been changed
4239 */
4240 if (bnum < 0)
4241 {
4242 bnum2 = mf_trans_del(mfp, bnum);
4243 if (bnum != bnum2)
4244 {
4245 bnum = bnum2;
4246 pp->pb_pointer[idx].pe_bnum = bnum;
4247 dirty = TRUE;
4248 }
4249 }
4250
4251 break;
4252 }
4253 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004254 if (idx >= (int)pp->pb_count) // past the end: something wrong!
Bram Moolenaar071d4272004-06-13 20:20:40 +00004255 {
4256 if (lnum > buf->b_ml.ml_line_count)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004257 siemsg(_("E322: line number out of range: %ld past the end"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00004258 lnum - buf->b_ml.ml_line_count);
4259
4260 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004261 siemsg(_("E323: line count wrong in block %ld"), bnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004262 goto error_block;
4263 }
4264 if (action == ML_DELETE)
4265 {
4266 pp->pb_pointer[idx].pe_line_count--;
4267 dirty = TRUE;
4268 }
4269 else if (action == ML_INSERT)
4270 {
4271 pp->pb_pointer[idx].pe_line_count++;
4272 dirty = TRUE;
4273 }
4274 mf_put(mfp, hp, dirty, FALSE);
4275 }
4276
4277error_block:
4278 mf_put(mfp, hp, FALSE, FALSE);
4279error_noblock:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02004280 /*
4281 * If action is ML_DELETE or ML_INSERT we have to correct the tree for
4282 * the incremented/decremented line counts, because there won't be a line
4283 * inserted/deleted after all.
4284 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004285 if (action == ML_DELETE)
4286 ml_lineadd(buf, 1);
4287 else if (action == ML_INSERT)
4288 ml_lineadd(buf, -1);
4289 buf->b_ml.ml_stack_top = 0;
4290 return NULL;
4291}
4292
4293/*
4294 * add an entry to the info pointer stack
4295 *
4296 * return -1 for failure, number of the new entry otherwise
4297 */
4298 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004299ml_add_stack(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004300{
4301 int top;
4302 infoptr_T *newstack;
4303
4304 top = buf->b_ml.ml_stack_top;
4305
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004306 // may have to increase the stack size
Bram Moolenaar071d4272004-06-13 20:20:40 +00004307 if (top == buf->b_ml.ml_stack_size)
4308 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004309 CHECK(top > 0, _("Stack size increases")); // more than 5 levels???
Bram Moolenaar071d4272004-06-13 20:20:40 +00004310
Bram Moolenaarc799fe22019-05-28 23:08:19 +02004311 newstack = ALLOC_MULT(infoptr_T, buf->b_ml.ml_stack_size + STACK_INCR);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004312 if (newstack == NULL)
4313 return -1;
Bram Moolenaarfbd302f2015-08-08 18:23:46 +02004314 if (top > 0)
4315 mch_memmove(newstack, buf->b_ml.ml_stack,
Bram Moolenaar8c8de832008-06-24 22:58:06 +00004316 (size_t)top * sizeof(infoptr_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004317 vim_free(buf->b_ml.ml_stack);
4318 buf->b_ml.ml_stack = newstack;
4319 buf->b_ml.ml_stack_size += STACK_INCR;
4320 }
4321
4322 buf->b_ml.ml_stack_top++;
4323 return top;
4324}
4325
4326/*
4327 * Update the pointer blocks on the stack for inserted/deleted lines.
4328 * The stack itself is also updated.
4329 *
4330 * When a insert/delete line action fails, the line is not inserted/deleted,
4331 * but the pointer blocks have already been updated. That is fixed here by
4332 * walking through the stack.
4333 *
4334 * Count is the number of lines added, negative if lines have been deleted.
4335 */
4336 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004337ml_lineadd(buf_T *buf, int count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004338{
4339 int idx;
4340 infoptr_T *ip;
4341 PTR_BL *pp;
4342 memfile_T *mfp = buf->b_ml.ml_mfp;
4343 bhdr_T *hp;
4344
4345 for (idx = buf->b_ml.ml_stack_top - 1; idx >= 0; --idx)
4346 {
4347 ip = &(buf->b_ml.ml_stack[idx]);
4348 if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
4349 break;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004350 pp = (PTR_BL *)(hp->bh_data); // must be pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00004351 if (pp->pb_id != PTR_ID)
4352 {
4353 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004354 iemsg(_("E317: pointer block id wrong 2"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004355 break;
4356 }
4357 pp->pb_pointer[ip->ip_index].pe_line_count += count;
4358 ip->ip_high += count;
4359 mf_put(mfp, hp, TRUE, FALSE);
4360 }
4361}
4362
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004363#if defined(HAVE_READLINK) || defined(PROTO)
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004364/*
4365 * Resolve a symlink in the last component of a file name.
4366 * Note that f_resolve() does it for every part of the path, we don't do that
4367 * here.
4368 * If it worked returns OK and the resolved link in "buf[MAXPATHL]".
4369 * Otherwise returns FAIL.
4370 */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004371 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004372resolve_symlink(char_u *fname, char_u *buf)
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004373{
4374 char_u tmp[MAXPATHL];
4375 int ret;
4376 int depth = 0;
4377
4378 if (fname == NULL)
4379 return FAIL;
4380
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004381 // Put the result so far in tmp[], starting with the original name.
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004382 vim_strncpy(tmp, fname, MAXPATHL - 1);
4383
4384 for (;;)
4385 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004386 // Limit symlink depth to 100, catch recursive loops.
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004387 if (++depth == 100)
4388 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004389 semsg(_("E773: Symlink loop for \"%s\""), fname);
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004390 return FAIL;
4391 }
4392
4393 ret = readlink((char *)tmp, (char *)buf, MAXPATHL - 1);
4394 if (ret <= 0)
4395 {
Bram Moolenaarcc984262005-12-23 22:19:46 +00004396 if (errno == EINVAL || errno == ENOENT)
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004397 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004398 // Found non-symlink or not existing file, stop here.
4399 // When at the first level use the unmodified name, skip the
4400 // call to vim_FullName().
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004401 if (depth == 1)
4402 return FAIL;
4403
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004404 // Use the resolved name in tmp[].
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004405 break;
4406 }
4407
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004408 // There must be some error reading links, use original name.
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004409 return FAIL;
4410 }
4411 buf[ret] = NUL;
4412
4413 /*
4414 * Check whether the symlink is relative or absolute.
4415 * If it's relative, build a new path based on the directory
4416 * portion of the filename (if any) and the path the symlink
4417 * points to.
4418 */
4419 if (mch_isFullName(buf))
4420 STRCPY(tmp, buf);
4421 else
4422 {
4423 char_u *tail;
4424
4425 tail = gettail(tmp);
4426 if (STRLEN(tail) + STRLEN(buf) >= MAXPATHL)
4427 return FAIL;
4428 STRCPY(tail, buf);
4429 }
4430 }
4431
4432 /*
4433 * Try to resolve the full name of the file so that the swapfile name will
4434 * be consistent even when opening a relative symlink from different
4435 * working directories.
4436 */
4437 return vim_FullName(tmp, buf, MAXPATHL, TRUE);
4438}
4439#endif
4440
Bram Moolenaar071d4272004-06-13 20:20:40 +00004441/*
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004442 * Make swap file name out of the file name and a directory name.
4443 * Returns pointer to allocated memory or NULL.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004444 */
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004445 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004446makeswapname(
4447 char_u *fname,
4448 char_u *ffname UNUSED,
4449 buf_T *buf,
4450 char_u *dir_name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004451{
4452 char_u *r, *s;
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02004453 char_u *fname_res = fname;
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004454#ifdef HAVE_READLINK
4455 char_u fname_buf[MAXPATHL];
Bram Moolenaar5966ea12020-07-15 15:30:05 +02004456
4457 // Expand symlink in the file name, so that we put the swap file with the
4458 // actual file instead of with the symlink.
4459 if (resolve_symlink(fname, fname_buf) == OK)
4460 fname_res = fname_buf;
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004461#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004462
Bram Moolenaar4f974752019-02-17 17:44:42 +01004463#if defined(UNIX) || defined(MSWIN) // Need _very_ long file names
Bram Moolenaarb113c3a2017-02-28 21:26:17 +01004464 int len = (int)STRLEN(dir_name);
Bram Moolenaarc525e3a2017-02-18 16:59:02 +01004465
4466 s = dir_name + len;
4467 if (after_pathsep(dir_name, s) && len > 1 && s[-1] == s[-2])
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004468 { // Ends with '//', Use Full path
Bram Moolenaar071d4272004-06-13 20:20:40 +00004469 r = NULL;
Bram Moolenaar5966ea12020-07-15 15:30:05 +02004470 if ((s = make_percent_swname(dir_name, fname_res)) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004471 {
4472 r = modname(s, (char_u *)".swp", FALSE);
4473 vim_free(s);
4474 }
4475 return r;
4476 }
4477#endif
4478
4479 r = buf_modname(
Bram Moolenaar071d4272004-06-13 20:20:40 +00004480 (buf->b_p_sn || buf->b_shortname),
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004481 fname_res,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004482 (char_u *)
Bram Moolenaare60acc12011-05-10 16:41:25 +02004483#if defined(VMS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004484 "_swp",
4485#else
4486 ".swp",
4487#endif
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004488 // Prepend a '.' to the swap file name for the current directory.
Bram Moolenaar48e330a2016-02-23 14:53:34 +01004489 dir_name[0] == '.' && dir_name[1] == NUL);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004490 if (r == NULL) // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00004491 return NULL;
4492
4493 s = get_file_in_dir(r, dir_name);
4494 vim_free(r);
4495 return s;
4496}
4497
4498/*
4499 * Get file name to use for swap file or backup file.
4500 * Use the name of the edited file "fname" and an entry in the 'dir' or 'bdir'
4501 * option "dname".
4502 * - If "dname" is ".", return "fname" (swap file in dir of file).
4503 * - If "dname" starts with "./", insert "dname" in "fname" (swap file
4504 * relative to dir of file).
4505 * - Otherwise, prepend "dname" to the tail of "fname" (swap file in specific
4506 * dir).
4507 *
4508 * The return value is an allocated string and can be NULL.
4509 */
4510 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004511get_file_in_dir(
4512 char_u *fname,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004513 char_u *dname) // don't use "dirname", it is a global for Alpha
Bram Moolenaar071d4272004-06-13 20:20:40 +00004514{
4515 char_u *t;
4516 char_u *tail;
4517 char_u *retval;
4518 int save_char;
4519
4520 tail = gettail(fname);
4521
4522 if (dname[0] == '.' && dname[1] == NUL)
4523 retval = vim_strsave(fname);
4524 else if (dname[0] == '.' && vim_ispathsep(dname[1]))
4525 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004526 if (tail == fname) // no path before file name
Bram Moolenaar071d4272004-06-13 20:20:40 +00004527 retval = concat_fnames(dname + 2, tail, TRUE);
4528 else
4529 {
4530 save_char = *tail;
4531 *tail = NUL;
4532 t = concat_fnames(fname, dname + 2, TRUE);
4533 *tail = save_char;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004534 if (t == NULL) // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00004535 retval = NULL;
4536 else
4537 {
4538 retval = concat_fnames(t, tail, TRUE);
4539 vim_free(t);
4540 }
4541 }
4542 }
4543 else
4544 retval = concat_fnames(dname, tail, TRUE);
4545
Bram Moolenaar4f974752019-02-17 17:44:42 +01004546#ifdef MSWIN
Bram Moolenaar69c35002013-11-04 02:54:12 +01004547 if (retval != NULL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01004548 for (t = gettail(retval); *t != NUL; MB_PTR_ADV(t))
Bram Moolenaar69c35002013-11-04 02:54:12 +01004549 if (*t == ':')
4550 *t = '%';
4551#endif
4552
Bram Moolenaar071d4272004-06-13 20:20:40 +00004553 return retval;
4554}
4555
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004556/*
4557 * Print the ATTENTION message: info about an existing swap file.
4558 */
4559 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004560attention_message(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004561 buf_T *buf, // buffer being edited
4562 char_u *fname) // swap file name
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004563{
Bram Moolenaar8767f522016-07-01 17:17:39 +02004564 stat_T st;
Bram Moolenaar63d25552019-05-10 21:28:38 +02004565 time_t swap_mtime;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004566
4567 ++no_wait_return;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004568 (void)emsg(_("E325: ATTENTION"));
Bram Moolenaar32526b32019-01-19 17:43:09 +01004569 msg_puts(_("\nFound a swap file by the name \""));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004570 msg_home_replace(fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004571 msg_puts("\"\n");
Bram Moolenaar63d25552019-05-10 21:28:38 +02004572 swap_mtime = swapfile_info(fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004573 msg_puts(_("While opening file \""));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004574 msg_outtrans(buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004575 msg_puts("\"\n");
Bram Moolenaard6105cb2018-10-13 19:06:27 +02004576 if (mch_stat((char *)buf->b_fname, &st) == -1)
4577 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004578 msg_puts(_(" CANNOT BE FOUND"));
Bram Moolenaard6105cb2018-10-13 19:06:27 +02004579 }
4580 else
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004581 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004582 msg_puts(_(" dated: "));
Bram Moolenaar63d25552019-05-10 21:28:38 +02004583 msg_puts(get_ctime(st.st_mtime, TRUE));
4584 if (swap_mtime != 0 && st.st_mtime > swap_mtime)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004585 msg_puts(_(" NEWER than swap file!\n"));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004586 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004587 // Some of these messages are long to allow translation to
4588 // other languages.
Bram Moolenaar32526b32019-01-19 17:43:09 +01004589 msg_puts(_("\n(1) Another program may be editing the same file. If this is the case,\n be careful not to end up with two different instances of the same\n file when making changes. Quit, or continue with caution.\n"));
4590 msg_puts(_("(2) An edit session for this file crashed.\n"));
4591 msg_puts(_(" If this is the case, use \":recover\" or \"vim -r "));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004592 msg_outtrans(buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004593 msg_puts(_("\"\n to recover the changes (see \":help recovery\").\n"));
4594 msg_puts(_(" If you did this already, delete the swap file \""));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004595 msg_outtrans(fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004596 msg_puts(_("\"\n to avoid this message.\n"));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004597 cmdline_row = msg_row;
4598 --no_wait_return;
4599}
4600
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01004601#if defined(FEAT_EVAL)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004602/*
4603 * Trigger the SwapExists autocommands.
4604 * Returns a value for equivalent to do_dialog() (see below):
4605 * 0: still need to ask for a choice
4606 * 1: open read-only
4607 * 2: edit anyway
4608 * 3: recover
4609 * 4: delete it
4610 * 5: quit
4611 * 6: abort
4612 */
4613 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004614do_swapexists(buf_T *buf, char_u *fname)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004615{
4616 set_vim_var_string(VV_SWAPNAME, fname, -1);
4617 set_vim_var_string(VV_SWAPCHOICE, NULL, -1);
4618
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004619 // Trigger SwapExists autocommands with <afile> set to the file being
4620 // edited. Disallow changing directory here.
Bram Moolenaar12c22ce2009-04-22 13:58:46 +00004621 ++allbuf_lock;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004622 apply_autocmds(EVENT_SWAPEXISTS, buf->b_fname, NULL, FALSE, NULL);
Bram Moolenaar12c22ce2009-04-22 13:58:46 +00004623 --allbuf_lock;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004624
4625 set_vim_var_string(VV_SWAPNAME, NULL, -1);
4626
4627 switch (*get_vim_var_str(VV_SWAPCHOICE))
4628 {
4629 case 'o': return 1;
4630 case 'e': return 2;
4631 case 'r': return 3;
4632 case 'd': return 4;
4633 case 'q': return 5;
4634 case 'a': return 6;
4635 }
4636
4637 return 0;
4638}
4639#endif
4640
Bram Moolenaar071d4272004-06-13 20:20:40 +00004641/*
4642 * Find out what name to use for the swap file for buffer 'buf'.
4643 *
4644 * Several names are tried to find one that does not exist
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004645 * Returns the name in allocated memory or NULL.
Bram Moolenaarf541c362011-10-26 11:44:18 +02004646 * When out of memory "dirp" is set to NULL.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004647 *
4648 * Note: If BASENAMELEN is not correct, you will get error messages for
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004649 * not being able to open the swap or undo file
Bram Moolenaar12c22ce2009-04-22 13:58:46 +00004650 * Note: May trigger SwapExists autocmd, pointers may change!
Bram Moolenaar071d4272004-06-13 20:20:40 +00004651 */
4652 static char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004653findswapname(
4654 buf_T *buf,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004655 char_u **dirp, // pointer to list of directories
4656 char_u *old_fname) // don't give warning for this file name
Bram Moolenaar071d4272004-06-13 20:20:40 +00004657{
4658 char_u *fname;
4659 int n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004660 char_u *dir_name;
4661#ifdef AMIGA
4662 BPTR fh;
4663#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004664 int r;
Bram Moolenaar69c35002013-11-04 02:54:12 +01004665 char_u *buf_fname = buf->b_fname;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004666
Bram Moolenaar48e330a2016-02-23 14:53:34 +01004667#if !defined(UNIX)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004668# define CREATE_DUMMY_FILE
4669 FILE *dummyfd = NULL;
4670
Bram Moolenaar4f974752019-02-17 17:44:42 +01004671# ifdef MSWIN
Bram Moolenaar69c35002013-11-04 02:54:12 +01004672 if (buf_fname != NULL && !mch_isFullName(buf_fname)
4673 && vim_strchr(gettail(buf_fname), ':'))
4674 {
4675 char_u *t;
4676
4677 buf_fname = vim_strsave(buf_fname);
4678 if (buf_fname == NULL)
4679 buf_fname = buf->b_fname;
4680 else
Bram Moolenaar91acfff2017-03-12 19:22:36 +01004681 for (t = gettail(buf_fname); *t != NUL; MB_PTR_ADV(t))
Bram Moolenaar69c35002013-11-04 02:54:12 +01004682 if (*t == ':')
4683 *t = '%';
4684 }
4685# endif
4686
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004687 /*
4688 * If we start editing a new file, e.g. "test.doc", which resides on an
4689 * MSDOS compatible filesystem, it is possible that the file
4690 * "test.doc.swp" which we create will be exactly the same file. To avoid
4691 * this problem we temporarily create "test.doc". Don't do this when the
4692 * check below for a 8.3 file name is used.
4693 */
Bram Moolenaar69c35002013-11-04 02:54:12 +01004694 if (!(buf->b_p_sn || buf->b_shortname) && buf_fname != NULL
4695 && mch_getperm(buf_fname) < 0)
4696 dummyfd = mch_fopen((char *)buf_fname, "w");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004697#endif
4698
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004699 /*
4700 * Isolate a directory name from *dirp and put it in dir_name.
4701 * First allocate some memory to put the directory name in.
4702 */
Bram Moolenaar964b3742019-05-24 18:54:09 +02004703 dir_name = alloc(STRLEN(*dirp) + 1);
Bram Moolenaarf541c362011-10-26 11:44:18 +02004704 if (dir_name == NULL)
4705 *dirp = NULL;
4706 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004707 (void)copy_option_part(dirp, dir_name, 31000, ",");
4708
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004709 /*
4710 * we try different names until we find one that does not exist yet
4711 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004712 if (dir_name == NULL) // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00004713 fname = NULL;
4714 else
Bram Moolenaar69c35002013-11-04 02:54:12 +01004715 fname = makeswapname(buf_fname, buf->b_ffname, buf, dir_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004716
4717 for (;;)
4718 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004719 if (fname == NULL) // must be out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00004720 break;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004721 if ((n = (int)STRLEN(fname)) == 0) // safety check
Bram Moolenaar071d4272004-06-13 20:20:40 +00004722 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01004723 VIM_CLEAR(fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004724 break;
4725 }
Bram Moolenaar48e330a2016-02-23 14:53:34 +01004726#if defined(UNIX)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004727/*
4728 * Some systems have a MS-DOS compatible filesystem that use 8.3 character
4729 * file names. If this is the first try and the swap file name does not fit in
4730 * 8.3, detect if this is the case, set shortname and try again.
4731 */
4732 if (fname[n - 2] == 'w' && fname[n - 1] == 'p'
4733 && !(buf->b_p_sn || buf->b_shortname))
4734 {
4735 char_u *tail;
4736 char_u *fname2;
Bram Moolenaar8767f522016-07-01 17:17:39 +02004737 stat_T s1, s2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004738 int f1, f2;
4739 int created1 = FALSE, created2 = FALSE;
4740 int same = FALSE;
4741
4742 /*
4743 * Check if swapfile name does not fit in 8.3:
4744 * It either contains two dots, is longer than 8 chars, or starts
4745 * with a dot.
4746 */
Bram Moolenaar69c35002013-11-04 02:54:12 +01004747 tail = gettail(buf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004748 if ( vim_strchr(tail, '.') != NULL
4749 || STRLEN(tail) > (size_t)8
4750 || *gettail(fname) == '.')
4751 {
4752 fname2 = alloc(n + 2);
4753 if (fname2 != NULL)
4754 {
4755 STRCPY(fname2, fname);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004756 // if fname == "xx.xx.swp", fname2 = "xx.xx.swx"
4757 // if fname == ".xx.swp", fname2 = ".xx.swpx"
4758 // if fname == "123456789.swp", fname2 = "12345678x.swp"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004759 if (vim_strchr(tail, '.') != NULL)
4760 fname2[n - 1] = 'x';
4761 else if (*gettail(fname) == '.')
4762 {
4763 fname2[n] = 'x';
4764 fname2[n + 1] = NUL;
4765 }
4766 else
4767 fname2[n - 5] += 1;
4768 /*
4769 * may need to create the files to be able to use mch_stat()
4770 */
4771 f1 = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
4772 if (f1 < 0)
4773 {
4774 f1 = mch_open_rw((char *)fname,
4775 O_RDWR|O_CREAT|O_EXCL|O_EXTRA);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004776 created1 = TRUE;
4777 }
4778 if (f1 >= 0)
4779 {
4780 f2 = mch_open((char *)fname2, O_RDONLY | O_EXTRA, 0);
4781 if (f2 < 0)
4782 {
4783 f2 = mch_open_rw((char *)fname2,
4784 O_RDWR|O_CREAT|O_EXCL|O_EXTRA);
4785 created2 = TRUE;
4786 }
4787 if (f2 >= 0)
4788 {
4789 /*
4790 * Both files exist now. If mch_stat() returns the
4791 * same device and inode they are the same file.
4792 */
4793 if (mch_fstat(f1, &s1) != -1
4794 && mch_fstat(f2, &s2) != -1
4795 && s1.st_dev == s2.st_dev
4796 && s1.st_ino == s2.st_ino)
4797 same = TRUE;
4798 close(f2);
4799 if (created2)
4800 mch_remove(fname2);
4801 }
4802 close(f1);
4803 if (created1)
4804 mch_remove(fname);
4805 }
4806 vim_free(fname2);
4807 if (same)
4808 {
4809 buf->b_shortname = TRUE;
4810 vim_free(fname);
Bram Moolenaar69c35002013-11-04 02:54:12 +01004811 fname = makeswapname(buf_fname, buf->b_ffname,
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004812 buf, dir_name);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004813 continue; // try again with b_shortname set
Bram Moolenaar071d4272004-06-13 20:20:40 +00004814 }
4815 }
4816 }
4817 }
4818#endif
4819 /*
4820 * check if the swapfile already exists
4821 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004822 if (mch_getperm(fname) < 0) // it does not exist
Bram Moolenaar071d4272004-06-13 20:20:40 +00004823 {
4824#ifdef HAVE_LSTAT
Bram Moolenaar8767f522016-07-01 17:17:39 +02004825 stat_T sb;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004826
4827 /*
4828 * Extra security check: When a swap file is a symbolic link, this
4829 * is most likely a symlink attack.
4830 */
4831 if (mch_lstat((char *)fname, &sb) < 0)
4832#else
4833# ifdef AMIGA
4834 fh = Open((UBYTE *)fname, (long)MODE_NEWFILE);
4835 /*
4836 * on the Amiga mch_getperm() will return -1 when the file exists
4837 * but is being used by another program. This happens if you edit
4838 * a file twice.
4839 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004840 if (fh != (BPTR)NULL) // can open file, OK
Bram Moolenaar071d4272004-06-13 20:20:40 +00004841 {
4842 Close(fh);
4843 mch_remove(fname);
4844 break;
4845 }
4846 if (IoErr() != ERROR_OBJECT_IN_USE
4847 && IoErr() != ERROR_OBJECT_EXISTS)
4848# endif
4849#endif
4850 break;
4851 }
4852
4853 /*
4854 * A file name equal to old_fname is OK to use.
4855 */
4856 if (old_fname != NULL && fnamecmp(fname, old_fname) == 0)
4857 break;
4858
4859 /*
4860 * get here when file already exists
4861 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004862 if (fname[n - 2] == 'w' && fname[n - 1] == 'p') // first try
Bram Moolenaar071d4272004-06-13 20:20:40 +00004863 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00004864 /*
4865 * on MS-DOS compatible filesystems (e.g. messydos) file.doc.swp
4866 * and file.doc are the same file. To guess if this problem is
4867 * present try if file.doc.swx exists. If it does, we set
4868 * buf->b_shortname and try file_doc.swp (dots replaced by
4869 * underscores for this file), and try again. If it doesn't we
4870 * assume that "file.doc.swp" already exists.
4871 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004872 if (!(buf->b_p_sn || buf->b_shortname)) // not tried yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00004873 {
4874 fname[n - 1] = 'x';
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004875 r = mch_getperm(fname); // try "file.swx"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004876 fname[n - 1] = 'p';
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004877 if (r >= 0) // "file.swx" seems to exist
Bram Moolenaar071d4272004-06-13 20:20:40 +00004878 {
4879 buf->b_shortname = TRUE;
4880 vim_free(fname);
Bram Moolenaar69c35002013-11-04 02:54:12 +01004881 fname = makeswapname(buf_fname, buf->b_ffname,
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004882 buf, dir_name);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004883 continue; // try again with '.' replaced with '_'
Bram Moolenaar071d4272004-06-13 20:20:40 +00004884 }
4885 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004886 /*
4887 * If we get here the ".swp" file really exists.
4888 * Give an error message, unless recovering, no file name, we are
4889 * viewing a help file or when the path of the file is different
4890 * (happens when all .swp files are in one directory).
4891 */
Bram Moolenaar69c35002013-11-04 02:54:12 +01004892 if (!recoverymode && buf_fname != NULL
Bram Moolenaar2debf1c2019-08-04 20:44:19 +02004893 && !buf->b_help
4894 && !(buf->b_flags & (BF_DUMMY | BF_NO_SEA)))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004895 {
4896 int fd;
4897 struct block0 b0;
4898 int differ = FALSE;
4899
4900 /*
4901 * Try to read block 0 from the swap file to get the original
4902 * file name (and inode number).
4903 */
4904 fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
4905 if (fd >= 0)
4906 {
Bram Moolenaar540fc6f2010-12-17 16:27:16 +01004907 if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004908 {
4909 /*
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004910 * If the swapfile has the same directory as the
4911 * buffer don't compare the directory names, they can
4912 * have a different mountpoint.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004913 */
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004914 if (b0.b0_flags & B0_SAME_DIR)
4915 {
4916 if (fnamecmp(gettail(buf->b_ffname),
4917 gettail(b0.b0_fname)) != 0
4918 || !same_directory(fname, buf->b_ffname))
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004919 {
4920#ifdef CHECK_INODE
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004921 // Symlinks may point to the same file even
4922 // when the name differs, need to check the
4923 // inode too.
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004924 expand_env(b0.b0_fname, NameBuff, MAXPATHL);
4925 if (fnamecmp_ino(buf->b_ffname, NameBuff,
4926 char_to_long(b0.b0_ino)))
4927#endif
4928 differ = TRUE;
4929 }
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004930 }
4931 else
4932 {
4933 /*
4934 * The name in the swap file may be
4935 * "~user/path/file". Expand it first.
4936 */
4937 expand_env(b0.b0_fname, NameBuff, MAXPATHL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004938#ifdef CHECK_INODE
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004939 if (fnamecmp_ino(buf->b_ffname, NameBuff,
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004940 char_to_long(b0.b0_ino)))
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004941 differ = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004942#else
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004943 if (fnamecmp(NameBuff, buf->b_ffname) != 0)
4944 differ = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004945#endif
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004946 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004947 }
4948 close(fd);
4949 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004950
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004951 // give the ATTENTION message when there is an old swap file
4952 // for the current file, and the buffer was not recovered.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004953 if (differ == FALSE && !(curbuf->b_flags & BF_RECOVERED)
4954 && vim_strchr(p_shm, SHM_ATTENTION) == NULL)
4955 {
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004956 int choice = 0;
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02004957 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004958#ifdef CREATE_DUMMY_FILE
4959 int did_use_dummy = FALSE;
4960
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004961 // Avoid getting a warning for the file being created
4962 // outside of Vim, it was created at the start of this
4963 // function. Delete the file now, because Vim might exit
4964 // here if the window is closed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004965 if (dummyfd != NULL)
4966 {
4967 fclose(dummyfd);
4968 dummyfd = NULL;
Bram Moolenaar69c35002013-11-04 02:54:12 +01004969 mch_remove(buf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004970 did_use_dummy = TRUE;
4971 }
4972#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004973
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02004974#ifdef HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar071d4272004-06-13 20:20:40 +00004975 process_still_running = FALSE;
4976#endif
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02004977 // It's safe to delete the swap file if all these are true:
4978 // - the edited file exists
4979 // - the swap file has no changes and looks OK
4980 if (mch_stat((char *)buf->b_fname, &st) == 0
4981 && swapfile_unchanged(fname))
4982 {
4983 choice = 4;
4984 if (p_verbose > 0)
4985 verb_msg(_("Found a swap file that is not useful, deleting it"));
4986 }
4987
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01004988#if defined(FEAT_EVAL)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004989 /*
4990 * If there is an SwapExists autocommand and we can handle
4991 * the response, trigger it. It may return 0 to ask the
4992 * user anyway.
4993 */
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02004994 if (choice == 0
4995 && swap_exists_action != SEA_NONE
Bram Moolenaar69c35002013-11-04 02:54:12 +01004996 && has_autocmd(EVENT_SWAPEXISTS, buf_fname, buf))
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004997 choice = do_swapexists(buf, fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004998
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004999 if (choice == 0)
5000#endif
5001 {
5002#ifdef FEAT_GUI
Bram Moolenaar798184c2018-10-07 20:48:39 +02005003 // If we are supposed to start the GUI but it wasn't
5004 // completely started yet, start it now. This makes
5005 // the messages displayed in the Vim window when
5006 // loading a session from the .gvimrc file.
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005007 if (gui.starting && !gui.in_use)
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005008 gui_start(NULL);
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005009#endif
Bram Moolenaar798184c2018-10-07 20:48:39 +02005010 // Show info about the existing swap file.
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005011 attention_message(buf, fname);
5012
Bram Moolenaar798184c2018-10-07 20:48:39 +02005013 // We don't want a 'q' typed at the more-prompt
5014 // interrupt loading a file.
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005015 got_int = FALSE;
Bram Moolenaar798184c2018-10-07 20:48:39 +02005016
5017 // If vimrc has "simalt ~x" we don't want it to
5018 // interfere with the prompt here.
Bram Moolenaar6a2633b2018-10-07 23:16:36 +02005019 flush_buffers(FLUSH_TYPEAHEAD);
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005020 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005021
5022#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005023 if (swap_exists_action != SEA_NONE && choice == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005024 {
5025 char_u *name;
5026
Bram Moolenaar964b3742019-05-24 18:54:09 +02005027 name = alloc(STRLEN(fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005028 + STRLEN(_("Swap file \""))
Bram Moolenaar964b3742019-05-24 18:54:09 +02005029 + STRLEN(_("\" already exists!")) + 5);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005030 if (name != NULL)
5031 {
5032 STRCPY(name, _("Swap file \""));
5033 home_replace(NULL, fname, name + STRLEN(name),
5034 1000, TRUE);
5035 STRCAT(name, _("\" already exists!"));
5036 }
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005037 choice = do_dialog(VIM_WARNING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005038 (char_u *)_("VIM - ATTENTION"),
5039 name == NULL
5040 ? (char_u *)_("Swap file already exists!")
5041 : name,
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02005042# ifdef HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar071d4272004-06-13 20:20:40 +00005043 process_still_running
5044 ? (char_u *)_("&Open Read-Only\n&Edit anyway\n&Recover\n&Quit\n&Abort") :
5045# endif
Bram Moolenaard2c340a2011-01-17 20:08:11 +01005046 (char_u *)_("&Open Read-Only\n&Edit anyway\n&Recover\n&Delete it\n&Quit\n&Abort"), 1, NULL, FALSE);
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005047
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02005048# ifdef HAVE_PROCESS_STILL_RUNNING
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005049 if (process_still_running && choice >= 4)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005050 choice++; // Skip missing "Delete it" button
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005051# endif
5052 vim_free(name);
5053
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005054 // pretend screen didn't scroll, need redraw anyway
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005055 msg_scrolled = 0;
5056 redraw_all_later(NOT_VALID);
5057 }
5058#endif
5059
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005060 if (choice > 0)
5061 {
5062 switch (choice)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005063 {
5064 case 1:
5065 buf->b_p_ro = TRUE;
5066 break;
5067 case 2:
5068 break;
5069 case 3:
5070 swap_exists_action = SEA_RECOVER;
5071 break;
5072 case 4:
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005073 mch_remove(fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005074 break;
5075 case 5:
5076 swap_exists_action = SEA_QUIT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005077 break;
5078 case 6:
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005079 swap_exists_action = SEA_QUIT;
5080 got_int = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005081 break;
5082 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005083
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005084 // If the file was deleted this fname can be used.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005085 if (mch_getperm(fname) < 0)
5086 break;
5087 }
5088 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005089 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01005090 msg_puts("\n");
Bram Moolenaar4770d092006-01-12 23:22:24 +00005091 if (msg_silent == 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005092 // call wait_return() later
Bram Moolenaar4770d092006-01-12 23:22:24 +00005093 need_wait_return = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005094 }
5095
5096#ifdef CREATE_DUMMY_FILE
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005097 // Going to try another name, need the dummy file again.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005098 if (did_use_dummy)
Bram Moolenaar69c35002013-11-04 02:54:12 +01005099 dummyfd = mch_fopen((char *)buf_fname, "w");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005100#endif
5101 }
5102 }
5103 }
5104
5105 /*
5106 * Change the ".swp" extension to find another file that can be used.
5107 * First decrement the last char: ".swo", ".swn", etc.
5108 * If that still isn't enough decrement the last but one char: ".svz"
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00005109 * Can happen when editing many "No Name" buffers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005110 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005111 if (fname[n - 1] == 'a') // ".s?a"
Bram Moolenaar071d4272004-06-13 20:20:40 +00005112 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005113 if (fname[n - 2] == 'a') // ".saa": tried enough, give up
Bram Moolenaar071d4272004-06-13 20:20:40 +00005114 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005115 emsg(_("E326: Too many swap files found"));
Bram Moolenaard23a8232018-02-10 18:45:26 +01005116 VIM_CLEAR(fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005117 break;
5118 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005119 --fname[n - 2]; // ".svz", ".suz", etc.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005120 fname[n - 1] = 'z' + 1;
5121 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005122 --fname[n - 1]; // ".swo", ".swn", etc.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005123 }
5124
5125 vim_free(dir_name);
5126#ifdef CREATE_DUMMY_FILE
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005127 if (dummyfd != NULL) // file has been created temporarily
Bram Moolenaar071d4272004-06-13 20:20:40 +00005128 {
5129 fclose(dummyfd);
Bram Moolenaar69c35002013-11-04 02:54:12 +01005130 mch_remove(buf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005131 }
5132#endif
Bram Moolenaar4f974752019-02-17 17:44:42 +01005133#ifdef MSWIN
Bram Moolenaar69c35002013-11-04 02:54:12 +01005134 if (buf_fname != buf->b_fname)
5135 vim_free(buf_fname);
5136#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005137 return fname;
5138}
5139
5140 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005141b0_magic_wrong(ZERO_BL *b0p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005142{
5143 return (b0p->b0_magic_long != (long)B0_MAGIC_LONG
5144 || b0p->b0_magic_int != (int)B0_MAGIC_INT
5145 || b0p->b0_magic_short != (short)B0_MAGIC_SHORT
5146 || b0p->b0_magic_char != B0_MAGIC_CHAR);
5147}
5148
5149#ifdef CHECK_INODE
5150/*
5151 * Compare current file name with file name from swap file.
5152 * Try to use inode numbers when possible.
5153 * Return non-zero when files are different.
5154 *
5155 * When comparing file names a few things have to be taken into consideration:
5156 * - When working over a network the full path of a file depends on the host.
5157 * We check the inode number if possible. It is not 100% reliable though,
5158 * because the device number cannot be used over a network.
5159 * - When a file does not exist yet (editing a new file) there is no inode
5160 * number.
5161 * - The file name in a swap file may not be valid on the current host. The
5162 * "~user" form is used whenever possible to avoid this.
5163 *
5164 * This is getting complicated, let's make a table:
5165 *
5166 * ino_c ino_s fname_c fname_s differ =
5167 *
5168 * both files exist -> compare inode numbers:
5169 * != 0 != 0 X X ino_c != ino_s
5170 *
5171 * inode number(s) unknown, file names available -> compare file names
5172 * == 0 X OK OK fname_c != fname_s
5173 * X == 0 OK OK fname_c != fname_s
5174 *
5175 * current file doesn't exist, file for swap file exist, file name(s) not
5176 * available -> probably different
5177 * == 0 != 0 FAIL X TRUE
5178 * == 0 != 0 X FAIL TRUE
5179 *
5180 * current file exists, inode for swap unknown, file name(s) not
5181 * available -> probably different
5182 * != 0 == 0 FAIL X TRUE
5183 * != 0 == 0 X FAIL TRUE
5184 *
5185 * current file doesn't exist, inode for swap unknown, one file name not
5186 * available -> probably different
5187 * == 0 == 0 FAIL OK TRUE
5188 * == 0 == 0 OK FAIL TRUE
5189 *
5190 * current file doesn't exist, inode for swap unknown, both file names not
Bram Moolenaar8c3169c2018-05-12 17:04:12 +02005191 * available -> compare file names
5192 * == 0 == 0 FAIL FAIL fname_c != fname_s
Bram Moolenaar071d4272004-06-13 20:20:40 +00005193 *
5194 * Note that when the ino_t is 64 bits, only the last 32 will be used. This
5195 * can't be changed without making the block 0 incompatible with 32 bit
5196 * versions.
5197 */
5198
5199 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005200fnamecmp_ino(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005201 char_u *fname_c, // current file name
5202 char_u *fname_s, // file name from swap file
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005203 long ino_block0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005204{
Bram Moolenaar8767f522016-07-01 17:17:39 +02005205 stat_T st;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005206 ino_t ino_c = 0; // ino of current file
5207 ino_t ino_s; // ino of file from swap file
5208 char_u buf_c[MAXPATHL]; // full path of fname_c
5209 char_u buf_s[MAXPATHL]; // full path of fname_s
5210 int retval_c; // flag: buf_c valid
5211 int retval_s; // flag: buf_s valid
Bram Moolenaar071d4272004-06-13 20:20:40 +00005212
5213 if (mch_stat((char *)fname_c, &st) == 0)
5214 ino_c = (ino_t)st.st_ino;
5215
5216 /*
5217 * First we try to get the inode from the file name, because the inode in
5218 * the swap file may be outdated. If that fails (e.g. this path is not
5219 * valid on this machine), use the inode from block 0.
5220 */
5221 if (mch_stat((char *)fname_s, &st) == 0)
5222 ino_s = (ino_t)st.st_ino;
5223 else
5224 ino_s = (ino_t)ino_block0;
5225
5226 if (ino_c && ino_s)
5227 return (ino_c != ino_s);
5228
5229 /*
5230 * One of the inode numbers is unknown, try a forced vim_FullName() and
5231 * compare the file names.
5232 */
5233 retval_c = vim_FullName(fname_c, buf_c, MAXPATHL, TRUE);
5234 retval_s = vim_FullName(fname_s, buf_s, MAXPATHL, TRUE);
5235 if (retval_c == OK && retval_s == OK)
Bram Moolenaar8c3169c2018-05-12 17:04:12 +02005236 return STRCMP(buf_c, buf_s) != 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005237
5238 /*
5239 * Can't compare inodes or file names, guess that the files are different,
Bram Moolenaar8c3169c2018-05-12 17:04:12 +02005240 * unless both appear not to exist at all, then compare with the file name
5241 * in the swap file.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005242 */
5243 if (ino_s == 0 && ino_c == 0 && retval_c == FAIL && retval_s == FAIL)
Bram Moolenaar8c3169c2018-05-12 17:04:12 +02005244 return STRCMP(fname_c, fname_s) != 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005245 return TRUE;
5246}
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005247#endif // CHECK_INODE
Bram Moolenaar071d4272004-06-13 20:20:40 +00005248
5249/*
5250 * Move a long integer into a four byte character array.
5251 * Used for machine independency in block zero.
5252 */
5253 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005254long_to_char(long n, char_u *s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005255{
5256 s[0] = (char_u)(n & 0xff);
5257 n = (unsigned)n >> 8;
5258 s[1] = (char_u)(n & 0xff);
5259 n = (unsigned)n >> 8;
5260 s[2] = (char_u)(n & 0xff);
5261 n = (unsigned)n >> 8;
5262 s[3] = (char_u)(n & 0xff);
5263}
5264
5265 static long
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005266char_to_long(char_u *s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005267{
5268 long retval;
5269
5270 retval = s[3];
5271 retval <<= 8;
5272 retval |= s[2];
5273 retval <<= 8;
5274 retval |= s[1];
5275 retval <<= 8;
5276 retval |= s[0];
5277
5278 return retval;
5279}
5280
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005281/*
5282 * Set the flags in the first block of the swap file:
5283 * - file is modified or not: buf->b_changed
5284 * - 'fileformat'
5285 * - 'fileencoding'
5286 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005287 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005288ml_setflags(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005289{
5290 bhdr_T *hp;
5291 ZERO_BL *b0p;
5292
5293 if (!buf->b_ml.ml_mfp)
5294 return;
5295 for (hp = buf->b_ml.ml_mfp->mf_used_last; hp != NULL; hp = hp->bh_prev)
5296 {
5297 if (hp->bh_bnum == 0)
5298 {
5299 b0p = (ZERO_BL *)(hp->bh_data);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005300 b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0;
5301 b0p->b0_flags = (b0p->b0_flags & ~B0_FF_MASK)
5302 | (get_fileformat(buf) + 1);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005303 add_b0_fenc(b0p, buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005304 hp->bh_flags |= BH_DIRTY;
5305 mf_sync(buf->b_ml.ml_mfp, MFS_ZERO);
5306 break;
5307 }
5308 }
5309}
5310
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005311#if defined(FEAT_CRYPT) || defined(PROTO)
5312/*
5313 * If "data" points to a data block encrypt the text in it and return a copy
5314 * in allocated memory. Return NULL when out of memory.
5315 * Otherwise return "data".
5316 */
5317 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005318ml_encrypt_data(
5319 memfile_T *mfp,
5320 char_u *data,
Bram Moolenaar8767f522016-07-01 17:17:39 +02005321 off_T offset,
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005322 unsigned size)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005323{
5324 DATA_BL *dp = (DATA_BL *)data;
5325 char_u *head_end;
5326 char_u *text_start;
5327 char_u *new_data;
5328 int text_len;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005329 cryptstate_T *state;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005330
5331 if (dp->db_id != DATA_ID)
5332 return data;
5333
Bram Moolenaarbc563362015-06-09 18:35:25 +02005334 state = ml_crypt_prepare(mfp, offset, FALSE);
5335 if (state == NULL)
5336 return data;
5337
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005338 new_data = alloc(size);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005339 if (new_data == NULL)
5340 return NULL;
5341 head_end = (char_u *)(&dp->db_index[dp->db_line_count]);
5342 text_start = (char_u *)dp + dp->db_txt_start;
5343 text_len = size - dp->db_txt_start;
5344
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005345 // Copy the header and the text.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005346 mch_memmove(new_data, dp, head_end - (char_u *)dp);
5347
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005348 // Encrypt the text.
Christian Brabandtf573c6e2021-06-20 14:02:16 +02005349 crypt_encode(state, text_start, text_len, new_data + dp->db_txt_start,
5350 FALSE);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005351 crypt_free_state(state);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005352
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005353 // Clear the gap.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005354 if (head_end < text_start)
5355 vim_memset(new_data + (head_end - data), 0, text_start - head_end);
5356
5357 return new_data;
5358}
5359
5360/*
Bram Moolenaarbc563362015-06-09 18:35:25 +02005361 * Decrypt the text in "data" if it points to an encrypted data block.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005362 */
5363 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005364ml_decrypt_data(
5365 memfile_T *mfp,
5366 char_u *data,
Bram Moolenaar8767f522016-07-01 17:17:39 +02005367 off_T offset,
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005368 unsigned size)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005369{
5370 DATA_BL *dp = (DATA_BL *)data;
5371 char_u *head_end;
5372 char_u *text_start;
5373 int text_len;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005374 cryptstate_T *state;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005375
5376 if (dp->db_id == DATA_ID)
5377 {
5378 head_end = (char_u *)(&dp->db_index[dp->db_line_count]);
5379 text_start = (char_u *)dp + dp->db_txt_start;
5380 text_len = dp->db_txt_end - dp->db_txt_start;
5381
5382 if (head_end > text_start || dp->db_txt_start > size
5383 || dp->db_txt_end > size)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005384 return; // data was messed up
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005385
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005386 state = ml_crypt_prepare(mfp, offset, TRUE);
Bram Moolenaarbc563362015-06-09 18:35:25 +02005387 if (state != NULL)
5388 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005389 // Decrypt the text in place.
Christian Brabandtf573c6e2021-06-20 14:02:16 +02005390 crypt_decode_inplace(state, text_start, text_len, FALSE);
Bram Moolenaarbc563362015-06-09 18:35:25 +02005391 crypt_free_state(state);
5392 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005393 }
5394}
5395
5396/*
5397 * Prepare for encryption/decryption, using the key, seed and offset.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005398 * Return an allocated cryptstate_T *.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005399 */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005400 static cryptstate_T *
Bram Moolenaar8767f522016-07-01 17:17:39 +02005401ml_crypt_prepare(memfile_T *mfp, off_T offset, int reading)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005402{
5403 buf_T *buf = mfp->mf_buffer;
5404 char_u salt[50];
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005405 int method_nr;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005406 char_u *key;
5407 char_u *seed;
5408
5409 if (reading && mfp->mf_old_key != NULL)
5410 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005411 // Reading back blocks with the previous key/method/seed.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005412 method_nr = mfp->mf_old_cm;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005413 key = mfp->mf_old_key;
5414 seed = mfp->mf_old_seed;
5415 }
5416 else
5417 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005418 method_nr = crypt_get_method_nr(buf);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005419 key = buf->b_p_key;
5420 seed = mfp->mf_seed;
5421 }
Bram Moolenaarbc563362015-06-09 18:35:25 +02005422 if (*key == NUL)
5423 return NULL;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005424
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005425 if (method_nr == CRYPT_M_ZIP)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005426 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005427 // For PKzip: Append the offset to the key, so that we use a different
5428 // key for every block.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005429 vim_snprintf((char *)salt, sizeof(salt), "%s%ld", key, (long)offset);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005430 return crypt_create(method_nr, salt, NULL, 0, NULL, 0);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005431 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005432
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005433 // Using blowfish or better: add salt and seed. We use the byte offset
5434 // of the block for the salt.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005435 vim_snprintf((char *)salt, sizeof(salt), "%ld", (long)offset);
5436 return crypt_create(method_nr, key, salt, (int)STRLEN(salt),
Christian Brabandtf573c6e2021-06-20 14:02:16 +02005437 seed, MF_SEED_LEN);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005438}
5439
5440#endif
5441
5442
Bram Moolenaar071d4272004-06-13 20:20:40 +00005443#if defined(FEAT_BYTEOFF) || defined(PROTO)
5444
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005445#define MLCS_MAXL 800 // max no of lines in chunk
5446#define MLCS_MINL 400 // should be half of MLCS_MAXL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005447
5448/*
Bram Moolenaar0ad014c2010-07-25 14:00:46 +02005449 * Keep information for finding byte offset of a line, updtype may be one of:
Bram Moolenaar071d4272004-06-13 20:20:40 +00005450 * ML_CHNK_ADDLINE: Add len to parent chunk, possibly splitting it
5451 * Careful: ML_CHNK_ADDLINE may cause ml_find_line() to be called.
5452 * ML_CHNK_DELLINE: Subtract len from parent chunk, possibly deleting it
5453 * ML_CHNK_UPDLINE: Add len to parent chunk, as a signed entity.
5454 */
5455 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005456ml_updatechunk(
5457 buf_T *buf,
5458 linenr_T line,
5459 long len,
5460 int updtype)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005461{
5462 static buf_T *ml_upd_lastbuf = NULL;
5463 static linenr_T ml_upd_lastline;
5464 static linenr_T ml_upd_lastcurline;
5465 static int ml_upd_lastcurix;
5466
5467 linenr_T curline = ml_upd_lastcurline;
5468 int curix = ml_upd_lastcurix;
5469 long size;
5470 chunksize_T *curchnk;
5471 int rest;
5472 bhdr_T *hp;
5473 DATA_BL *dp;
5474
5475 if (buf->b_ml.ml_usedchunks == -1 || len == 0)
5476 return;
5477 if (buf->b_ml.ml_chunksize == NULL)
5478 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005479 buf->b_ml.ml_chunksize = ALLOC_MULT(chunksize_T, 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005480 if (buf->b_ml.ml_chunksize == NULL)
5481 {
5482 buf->b_ml.ml_usedchunks = -1;
5483 return;
5484 }
5485 buf->b_ml.ml_numchunks = 100;
5486 buf->b_ml.ml_usedchunks = 1;
5487 buf->b_ml.ml_chunksize[0].mlcs_numlines = 1;
5488 buf->b_ml.ml_chunksize[0].mlcs_totalsize = 1;
5489 }
5490
5491 if (updtype == ML_CHNK_UPDLINE && buf->b_ml.ml_line_count == 1)
5492 {
5493 /*
5494 * First line in empty buffer from ml_flush_line() -- reset
5495 */
5496 buf->b_ml.ml_usedchunks = 1;
5497 buf->b_ml.ml_chunksize[0].mlcs_numlines = 1;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01005498 buf->b_ml.ml_chunksize[0].mlcs_totalsize = (long)buf->b_ml.ml_line_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005499 return;
5500 }
5501
5502 /*
5503 * Find chunk that our line belongs to, curline will be at start of the
5504 * chunk.
5505 */
5506 if (buf != ml_upd_lastbuf || line != ml_upd_lastline + 1
5507 || updtype != ML_CHNK_ADDLINE)
5508 {
5509 for (curline = 1, curix = 0;
5510 curix < buf->b_ml.ml_usedchunks - 1
5511 && line >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines;
5512 curix++)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005513 curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005514 }
Bram Moolenaara9a8e042018-10-30 22:15:55 +01005515 else if (curix < buf->b_ml.ml_usedchunks - 1
5516 && line >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005517 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005518 // Adjust cached curix & curline
Bram Moolenaar071d4272004-06-13 20:20:40 +00005519 curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
5520 curix++;
5521 }
5522 curchnk = buf->b_ml.ml_chunksize + curix;
5523
5524 if (updtype == ML_CHNK_DELLINE)
Bram Moolenaar5a6404c2006-11-01 17:12:57 +00005525 len = -len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005526 curchnk->mlcs_totalsize += len;
5527 if (updtype == ML_CHNK_ADDLINE)
5528 {
5529 curchnk->mlcs_numlines++;
5530
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005531 // May resize here so we don't have to do it in both cases below
Bram Moolenaar071d4272004-06-13 20:20:40 +00005532 if (buf->b_ml.ml_usedchunks + 1 >= buf->b_ml.ml_numchunks)
5533 {
Bram Moolenaar9abd5c62015-02-10 18:34:01 +01005534 chunksize_T *t_chunksize = buf->b_ml.ml_chunksize;
5535
Bram Moolenaar071d4272004-06-13 20:20:40 +00005536 buf->b_ml.ml_numchunks = buf->b_ml.ml_numchunks * 3 / 2;
Bram Moolenaar51b6eb42020-08-22 15:19:18 +02005537 buf->b_ml.ml_chunksize = vim_realloc(buf->b_ml.ml_chunksize,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005538 sizeof(chunksize_T) * buf->b_ml.ml_numchunks);
5539 if (buf->b_ml.ml_chunksize == NULL)
5540 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005541 // Hmmmm, Give up on offset for this buffer
Bram Moolenaar9abd5c62015-02-10 18:34:01 +01005542 vim_free(t_chunksize);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005543 buf->b_ml.ml_usedchunks = -1;
5544 return;
5545 }
5546 }
5547
5548 if (buf->b_ml.ml_chunksize[curix].mlcs_numlines >= MLCS_MAXL)
5549 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005550 int count; // number of entries in block
Bram Moolenaar071d4272004-06-13 20:20:40 +00005551 int idx;
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005552 int end_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005553 int text_end;
5554 int linecnt;
5555
5556 mch_memmove(buf->b_ml.ml_chunksize + curix + 1,
5557 buf->b_ml.ml_chunksize + curix,
5558 (buf->b_ml.ml_usedchunks - curix) *
5559 sizeof(chunksize_T));
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005560 // Compute length of first half of lines in the split chunk
Bram Moolenaar071d4272004-06-13 20:20:40 +00005561 size = 0;
5562 linecnt = 0;
5563 while (curline < buf->b_ml.ml_line_count
5564 && linecnt < MLCS_MINL)
5565 {
5566 if ((hp = ml_find_line(buf, curline, ML_FIND)) == NULL)
5567 {
5568 buf->b_ml.ml_usedchunks = -1;
5569 return;
5570 }
5571 dp = (DATA_BL *)(hp->bh_data);
5572 count = (long)(buf->b_ml.ml_locked_high) -
5573 (long)(buf->b_ml.ml_locked_low) + 1;
5574 idx = curline - buf->b_ml.ml_locked_low;
5575 curline = buf->b_ml.ml_locked_high + 1;
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005576
5577 // compute index of last line to use in this MEMLINE
Bram Moolenaar071d4272004-06-13 20:20:40 +00005578 rest = count - idx;
5579 if (linecnt + rest > MLCS_MINL)
5580 {
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005581 end_idx = idx + MLCS_MINL - linecnt - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005582 linecnt = MLCS_MINL;
5583 }
5584 else
5585 {
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005586 end_idx = count - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005587 linecnt += rest;
5588 }
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01005589#ifdef FEAT_PROP_POPUP
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005590 if (buf->b_has_textprop)
5591 {
5592 int i;
5593
5594 // We cannot use the text pointers to get the text length,
5595 // the text prop info would also be counted. Go over the
5596 // lines.
5597 for (i = end_idx; i < idx; ++i)
Bram Moolenaar4b7214e2019-01-03 21:55:32 +01005598 size += (int)STRLEN((char_u *)dp + (dp->db_index[i] & DB_INDEX_MASK)) + 1;
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005599 }
5600 else
5601#endif
5602 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005603 if (idx == 0)// first line in block, text at the end
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005604 text_end = dp->db_txt_end;
5605 else
5606 text_end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK);
5607 size += text_end - ((dp->db_index[end_idx]) & DB_INDEX_MASK);
5608 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005609 }
5610 buf->b_ml.ml_chunksize[curix].mlcs_numlines = linecnt;
5611 buf->b_ml.ml_chunksize[curix + 1].mlcs_numlines -= linecnt;
5612 buf->b_ml.ml_chunksize[curix].mlcs_totalsize = size;
5613 buf->b_ml.ml_chunksize[curix + 1].mlcs_totalsize -= size;
5614 buf->b_ml.ml_usedchunks++;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005615 ml_upd_lastbuf = NULL; // Force recalc of curix & curline
Bram Moolenaar071d4272004-06-13 20:20:40 +00005616 return;
5617 }
5618 else if (buf->b_ml.ml_chunksize[curix].mlcs_numlines >= MLCS_MINL
5619 && curix == buf->b_ml.ml_usedchunks - 1
5620 && buf->b_ml.ml_line_count - line <= 1)
5621 {
5622 /*
Bram Moolenaar8e7d6222020-12-18 19:49:56 +01005623 * We are in the last chunk and it is cheap to create a new one
Bram Moolenaar071d4272004-06-13 20:20:40 +00005624 * after this. Do it now to avoid the loop above later on
5625 */
5626 curchnk = buf->b_ml.ml_chunksize + curix + 1;
5627 buf->b_ml.ml_usedchunks++;
5628 if (line == buf->b_ml.ml_line_count)
5629 {
5630 curchnk->mlcs_numlines = 0;
5631 curchnk->mlcs_totalsize = 0;
5632 }
5633 else
5634 {
5635 /*
5636 * Line is just prior to last, move count for last
5637 * This is the common case when loading a new file
5638 */
5639 hp = ml_find_line(buf, buf->b_ml.ml_line_count, ML_FIND);
5640 if (hp == NULL)
5641 {
5642 buf->b_ml.ml_usedchunks = -1;
5643 return;
5644 }
5645 dp = (DATA_BL *)(hp->bh_data);
5646 if (dp->db_line_count == 1)
5647 rest = dp->db_txt_end - dp->db_txt_start;
5648 else
5649 rest =
5650 ((dp->db_index[dp->db_line_count - 2]) & DB_INDEX_MASK)
5651 - dp->db_txt_start;
5652 curchnk->mlcs_totalsize = rest;
5653 curchnk->mlcs_numlines = 1;
5654 curchnk[-1].mlcs_totalsize -= rest;
5655 curchnk[-1].mlcs_numlines -= 1;
5656 }
5657 }
5658 }
5659 else if (updtype == ML_CHNK_DELLINE)
5660 {
5661 curchnk->mlcs_numlines--;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005662 ml_upd_lastbuf = NULL; // Force recalc of curix & curline
Bram Moolenaar071d4272004-06-13 20:20:40 +00005663 if (curix < (buf->b_ml.ml_usedchunks - 1)
5664 && (curchnk->mlcs_numlines + curchnk[1].mlcs_numlines)
5665 <= MLCS_MINL)
5666 {
5667 curix++;
5668 curchnk = buf->b_ml.ml_chunksize + curix;
5669 }
5670 else if (curix == 0 && curchnk->mlcs_numlines <= 0)
5671 {
5672 buf->b_ml.ml_usedchunks--;
5673 mch_memmove(buf->b_ml.ml_chunksize, buf->b_ml.ml_chunksize + 1,
5674 buf->b_ml.ml_usedchunks * sizeof(chunksize_T));
5675 return;
5676 }
5677 else if (curix == 0 || (curchnk->mlcs_numlines > 10
5678 && (curchnk->mlcs_numlines + curchnk[-1].mlcs_numlines)
5679 > MLCS_MINL))
5680 {
5681 return;
5682 }
5683
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005684 // Collapse chunks
Bram Moolenaar071d4272004-06-13 20:20:40 +00005685 curchnk[-1].mlcs_numlines += curchnk->mlcs_numlines;
5686 curchnk[-1].mlcs_totalsize += curchnk->mlcs_totalsize;
5687 buf->b_ml.ml_usedchunks--;
5688 if (curix < buf->b_ml.ml_usedchunks)
5689 {
5690 mch_memmove(buf->b_ml.ml_chunksize + curix,
5691 buf->b_ml.ml_chunksize + curix + 1,
5692 (buf->b_ml.ml_usedchunks - curix) *
5693 sizeof(chunksize_T));
5694 }
5695 return;
5696 }
5697 ml_upd_lastbuf = buf;
5698 ml_upd_lastline = line;
5699 ml_upd_lastcurline = curline;
5700 ml_upd_lastcurix = curix;
5701}
5702
5703/*
5704 * Find offset for line or line with offset.
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005705 * Find line with offset if "lnum" is 0; return remaining offset in offp
5706 * Find offset of line if "lnum" > 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00005707 * return -1 if information is not available
5708 */
5709 long
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005710ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005711{
5712 linenr_T curline;
5713 int curix;
5714 long size;
5715 bhdr_T *hp;
5716 DATA_BL *dp;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005717 int count; // number of entries in block
Bram Moolenaar071d4272004-06-13 20:20:40 +00005718 int idx;
5719 int start_idx;
5720 int text_end;
5721 long offset;
5722 int len;
5723 int ffdos = (get_fileformat(buf) == EOL_DOS);
5724 int extra = 0;
5725
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005726 // take care of cached line first
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005727 ml_flush_line(curbuf);
5728
Bram Moolenaar071d4272004-06-13 20:20:40 +00005729 if (buf->b_ml.ml_usedchunks == -1
5730 || buf->b_ml.ml_chunksize == NULL
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005731 || lnum < 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005732 return -1;
5733
5734 if (offp == NULL)
5735 offset = 0;
5736 else
5737 offset = *offp;
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005738 if (lnum == 0 && offset <= 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005739 return 1; // Not a "find offset" and offset 0 _must_ be in line 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00005740 /*
5741 * Find the last chunk before the one containing our line. Last chunk is
5742 * special because it will never qualify
5743 */
5744 curline = 1;
5745 curix = size = 0;
5746 while (curix < buf->b_ml.ml_usedchunks - 1
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005747 && ((lnum != 0
5748 && lnum >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005749 || (offset != 0
5750 && offset > size + buf->b_ml.ml_chunksize[curix].mlcs_totalsize
5751 + ffdos * buf->b_ml.ml_chunksize[curix].mlcs_numlines)))
5752 {
5753 curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
5754 size += buf->b_ml.ml_chunksize[curix].mlcs_totalsize;
5755 if (offset && ffdos)
5756 size += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
5757 curix++;
5758 }
5759
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005760 while ((lnum != 0 && curline < lnum) || (offset != 0 && size < offset))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005761 {
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005762#ifdef FEAT_PROP_POPUP
5763 size_t textprop_total = 0;
5764#endif
5765
Bram Moolenaar071d4272004-06-13 20:20:40 +00005766 if (curline > buf->b_ml.ml_line_count
5767 || (hp = ml_find_line(buf, curline, ML_FIND)) == NULL)
5768 return -1;
5769 dp = (DATA_BL *)(hp->bh_data);
5770 count = (long)(buf->b_ml.ml_locked_high) -
5771 (long)(buf->b_ml.ml_locked_low) + 1;
5772 start_idx = idx = curline - buf->b_ml.ml_locked_low;
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005773 if (idx == 0) // first line in block, text at the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00005774 text_end = dp->db_txt_end;
5775 else
5776 text_end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005777 // Compute index of last line to use in this MEMLINE
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005778 if (lnum != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005779 {
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005780 if (curline + (count - idx) >= lnum)
5781 idx += lnum - curline - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005782 else
5783 idx = count - 1;
5784 }
5785 else
5786 {
5787 extra = 0;
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005788 for (;;)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005789 {
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005790#ifdef FEAT_PROP_POPUP
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005791 size_t textprop_size = 0;
5792
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005793 if (buf->b_has_textprop)
5794 {
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005795 char_u *l1, *l2;
5796
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005797 // compensate for the extra bytes taken by textprops
5798 l1 = (char_u *)dp + ((dp->db_index[idx]) & DB_INDEX_MASK);
5799 l2 = (char_u *)dp + (idx == 0 ? dp->db_txt_end
5800 : ((dp->db_index[idx - 1]) & DB_INDEX_MASK));
5801 textprop_size = (l2 - l1) - (STRLEN(l1) + 1);
5802 }
5803#endif
5804 if (!(offset >= size
5805 + text_end - (int)((dp->db_index[idx]) & DB_INDEX_MASK)
5806#ifdef FEAT_PROP_POPUP
Bram Moolenaar94b6fb72020-01-17 21:00:59 +01005807 - (long)(textprop_total + textprop_size)
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005808#endif
5809 + ffdos))
5810 break;
5811
Bram Moolenaar071d4272004-06-13 20:20:40 +00005812 if (ffdos)
5813 size++;
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005814#ifdef FEAT_PROP_POPUP
5815 textprop_total += textprop_size;
5816#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005817 if (idx == count - 1)
5818 {
5819 extra = 1;
5820 break;
5821 }
5822 idx++;
5823 }
5824 }
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01005825#ifdef FEAT_PROP_POPUP
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005826 if (buf->b_has_textprop && lnum != 0)
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005827 {
5828 int i;
5829
5830 // cannot use the db_index pointer, need to get the actual text
5831 // lengths.
5832 len = 0;
5833 for (i = start_idx; i <= idx; ++i)
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005834 {
5835 char_u *p = (char_u *)dp + ((dp->db_index[i]) & DB_INDEX_MASK);
5836 len += (int)STRLEN(p) + 1;
5837 }
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005838 }
5839 else
5840#endif
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005841 len = text_end - ((dp->db_index[idx]) & DB_INDEX_MASK)
5842#ifdef FEAT_PROP_POPUP
5843 - (long)textprop_total
5844#endif
5845 ;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005846 size += len;
5847 if (offset != 0 && size >= offset)
5848 {
5849 if (size + ffdos == offset)
5850 *offp = 0;
5851 else if (idx == start_idx)
5852 *offp = offset - size + len;
5853 else
5854 *offp = offset - size + len
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005855 - (text_end - ((dp->db_index[idx - 1]) & DB_INDEX_MASK))
5856#ifdef FEAT_PROP_POPUP
5857 + (long)textprop_total
5858#endif
5859 ;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005860 curline += idx - start_idx + extra;
5861 if (curline > buf->b_ml.ml_line_count)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005862 return -1; // exactly one byte beyond the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00005863 return curline;
5864 }
5865 curline = buf->b_ml.ml_locked_high + 1;
5866 }
5867
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005868 if (lnum != 0)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005869 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005870 // Count extra CR characters.
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005871 if (ffdos)
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005872 size += lnum - 1;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005873
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005874 // Don't count the last line break if 'noeol' and ('bin' or
5875 // 'nofixeol').
Bram Moolenaar34d72d42015-07-17 14:18:08 +02005876 if ((!buf->b_p_fixeol || buf->b_p_bin) && !buf->b_p_eol
Bram Moolenaarc26f7c62018-08-20 22:53:04 +02005877 && lnum > buf->b_ml.ml_line_count)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005878 size -= ffdos + 1;
5879 }
5880
Bram Moolenaar071d4272004-06-13 20:20:40 +00005881 return size;
5882}
5883
5884/*
5885 * Goto byte in buffer with offset 'cnt'.
5886 */
5887 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005888goto_byte(long cnt)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005889{
5890 long boff = cnt;
5891 linenr_T lnum;
5892
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005893 ml_flush_line(curbuf); // cached line may be dirty
Bram Moolenaar071d4272004-06-13 20:20:40 +00005894 setpcmark();
5895 if (boff)
5896 --boff;
5897 lnum = ml_find_line_or_offset(curbuf, (linenr_T)0, &boff);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005898 if (lnum < 1) // past the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00005899 {
5900 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
5901 curwin->w_curswant = MAXCOL;
5902 coladvance((colnr_T)MAXCOL);
5903 }
5904 else
5905 {
5906 curwin->w_cursor.lnum = lnum;
5907 curwin->w_cursor.col = (colnr_T)boff;
Bram Moolenaar943d2b52005-12-02 00:50:49 +00005908 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005909 curwin->w_set_curswant = TRUE;
5910 }
5911 check_cursor();
5912
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005913 // Make sure the cursor is on the first byte of a multi-byte char.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005914 if (has_mbyte)
5915 mb_adjust_cursor();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005916}
5917#endif