blob: 0eac1cdcb1f7454bb010820516ba083863f4e62f [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
1107/*
1108 * Return TRUE if the process with number "b0p->b0_pid" is still running.
1109 * "swap_fname" is the name of the swap file, if it's from before a reboot then
1110 * the result is FALSE;
1111 */
1112 static int
1113swapfile_process_running(ZERO_BL *b0p, char_u *swap_fname UNUSED)
1114{
1115#ifdef HAVE_SYSINFO_UPTIME
1116 stat_T st;
1117 struct sysinfo sinfo;
1118
1119 // If the system rebooted after when the swap file was written then the
1120 // process can't be running now.
1121 if (mch_stat((char *)swap_fname, &st) != -1
1122 && sysinfo(&sinfo) == 0
Bram Moolenaar23b32a82021-03-10 21:55:46 +01001123 && st.st_mtime < time(NULL) - (
1124# ifdef FEAT_EVAL
1125 override_sysinfo_uptime >= 0 ? override_sysinfo_uptime :
1126# endif
1127 sinfo.uptime))
Bram Moolenaarf52f0602021-03-10 21:26:37 +01001128 return FALSE;
1129#endif
1130 return mch_process_running(char_to_long(b0p->b0_pid));
1131}
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001132
1133/*
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001134 * Try to recover curbuf from the .swp file.
Bram Moolenaar99499b12019-05-23 21:35:48 +02001135 * If "checkext" is TRUE, check the extension and detect whether it is
1136 * a swap file.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001137 */
1138 void
Bram Moolenaar99499b12019-05-23 21:35:48 +02001139ml_recover(int checkext)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001140{
1141 buf_T *buf = NULL;
1142 memfile_T *mfp = NULL;
1143 char_u *fname;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001144 char_u *fname_used = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001145 bhdr_T *hp = NULL;
1146 ZERO_BL *b0p;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001147 int b0_ff;
1148 char_u *b0_fenc = NULL;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001149#ifdef FEAT_CRYPT
1150 int b0_cm = -1;
1151#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001152 PTR_BL *pp;
1153 DATA_BL *dp;
1154 infoptr_T *ip;
1155 blocknr_T bnum;
1156 int page_count;
Bram Moolenaar8767f522016-07-01 17:17:39 +02001157 stat_T org_stat, swp_stat;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001158 int len;
1159 int directly;
1160 linenr_T lnum;
1161 char_u *p;
1162 int i;
1163 long error;
1164 int cannot_open;
1165 linenr_T line_count;
1166 int has_error;
1167 int idx;
1168 int top;
1169 int txt_start;
Bram Moolenaar8767f522016-07-01 17:17:39 +02001170 off_T size;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001171 int called_from_main;
1172 int serious_error = TRUE;
1173 long mtime;
1174 int attr;
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001175 int orig_file_status = NOTDONE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001176
1177 recoverymode = TRUE;
1178 called_from_main = (curbuf->b_ml.ml_mfp == NULL);
Bram Moolenaar8820b482017-03-16 17:23:31 +01001179 attr = HL_ATTR(HLF_E);
Bram Moolenaard0ba34a2009-11-03 12:06:23 +00001180
1181 /*
Bram Moolenaaraf903e52017-12-02 15:11:22 +01001182 * 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 +00001183 * Otherwise a search is done to find the swap file(s).
1184 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001185 fname = curbuf->b_fname;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001186 if (fname == NULL) // When there is no file name
Bram Moolenaar071d4272004-06-13 20:20:40 +00001187 fname = (char_u *)"";
1188 len = (int)STRLEN(fname);
Bram Moolenaar99499b12019-05-23 21:35:48 +02001189 if (checkext && len >= 4 &&
Bram Moolenaare60acc12011-05-10 16:41:25 +02001190#if defined(VMS)
Bram Moolenaar79518e22017-02-17 16:31:35 +01001191 STRNICMP(fname + len - 4, "_s", 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001192#else
Bram Moolenaar79518e22017-02-17 16:31:35 +01001193 STRNICMP(fname + len - 4, ".s", 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001194#endif
Bram Moolenaar79518e22017-02-17 16:31:35 +01001195 == 0
Bram Moolenaaraf903e52017-12-02 15:11:22 +01001196 && vim_strchr((char_u *)"abcdefghijklmnopqrstuvw",
1197 TOLOWER_ASC(fname[len - 2])) != NULL
Bram Moolenaard0ba34a2009-11-03 12:06:23 +00001198 && ASCII_ISALPHA(fname[len - 1]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001199 {
1200 directly = TRUE;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001201 fname_used = vim_strsave(fname); // make a copy for mf_open()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001202 }
1203 else
1204 {
1205 directly = FALSE;
1206
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001207 // count the number of matching swap files
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001208 len = recover_names(fname, FALSE, 0, NULL);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001209 if (len == 0) // no swap files found
Bram Moolenaar071d4272004-06-13 20:20:40 +00001210 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001211 semsg(_("E305: No swap file found for %s"), fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001212 goto theend;
1213 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001214 if (len == 1) // one swap file found, use it
Bram Moolenaar071d4272004-06-13 20:20:40 +00001215 i = 1;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001216 else // several swap files found, choose
Bram Moolenaar071d4272004-06-13 20:20:40 +00001217 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001218 // list the names of the swap files
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001219 (void)recover_names(fname, TRUE, 0, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001220 msg_putchar('\n');
Bram Moolenaar32526b32019-01-19 17:43:09 +01001221 msg_puts(_("Enter number of swap file to use (0 to quit): "));
Bram Moolenaar24bbcfe2005-06-28 23:32:02 +00001222 i = get_number(FALSE, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001223 if (i < 1 || i > len)
1224 goto theend;
1225 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001226 // get the swap file name that will be used
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001227 (void)recover_names(fname, FALSE, i, &fname_used);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001228 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001229 if (fname_used == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001230 goto theend; // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00001231
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001232 // When called from main() still need to initialize storage structure
Bram Moolenaar4770d092006-01-12 23:22:24 +00001233 if (called_from_main && ml_open(curbuf) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001234 getout(1);
1235
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001236 /*
1237 * Allocate a buffer structure for the swap file that is used for recovery.
Bram Moolenaar0ad014c2010-07-25 14:00:46 +02001238 * Only the memline and crypt information in it are really used.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001239 */
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001240 buf = ALLOC_ONE(buf_T);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001241 if (buf == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001242 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001243
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001244 /*
1245 * init fields in memline struct
1246 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001247 buf->b_ml.ml_stack_size = 0; // no stack yet
1248 buf->b_ml.ml_stack = NULL; // no stack yet
1249 buf->b_ml.ml_stack_top = 0; // nothing in the stack
1250 buf->b_ml.ml_line_lnum = 0; // no cached line
1251 buf->b_ml.ml_locked = NULL; // no locked block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001252 buf->b_ml.ml_flags = 0;
Bram Moolenaar0fe849a2010-07-25 15:11:11 +02001253#ifdef FEAT_CRYPT
1254 buf->b_p_key = empty_option;
1255 buf->b_p_cm = empty_option;
1256#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001257
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001258 /*
1259 * open the memfile from the old swap file
1260 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001261 p = vim_strsave(fname_used); // save "fname_used" for the message:
1262 // mf_open() will consume "fname_used"!
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001263 mfp = mf_open(fname_used, O_RDONLY);
1264 fname_used = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001265 if (mfp == NULL || mfp->mf_fd < 0)
1266 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001267 if (fname_used != NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001268 semsg(_("E306: Cannot open %s"), fname_used);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001269 goto theend;
1270 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001271 buf->b_ml.ml_mfp = mfp;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001272#ifdef FEAT_CRYPT
1273 mfp->mf_buffer = buf;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001274#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001275
1276 /*
1277 * The page size set in mf_open() might be different from the page size
1278 * used in the swap file, we must get it from block 0. But to read block
1279 * 0 we need a page size. Use the minimal size for block 0 here, it will
1280 * be set to the real value below.
1281 */
1282 mfp->mf_page_size = MIN_SWAP_PAGE_SIZE;
1283
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001284 /*
1285 * try to read block 0
1286 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001287 if ((hp = mf_get(mfp, (blocknr_T)0, 1)) == NULL)
1288 {
1289 msg_start();
Bram Moolenaar32526b32019-01-19 17:43:09 +01001290 msg_puts_attr(_("Unable to read block 0 from "), attr | MSG_HIST);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001291 msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001292 msg_puts_attr(_("\nMaybe no changes were made or Vim did not update the swap file."),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001293 attr | MSG_HIST);
1294 msg_end();
1295 goto theend;
1296 }
1297 b0p = (ZERO_BL *)(hp->bh_data);
1298 if (STRNCMP(b0p->b0_version, "VIM 3.0", 7) == 0)
1299 {
1300 msg_start();
1301 msg_outtrans_attr(mfp->mf_fname, MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001302 msg_puts_attr(_(" cannot be used with this version of Vim.\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001303 MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001304 msg_puts_attr(_("Use Vim version 3.0.\n"), MSG_HIST);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001305 msg_end();
1306 goto theend;
1307 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001308 if (ml_check_b0_id(b0p) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001309 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001310 semsg(_("E307: %s does not look like a Vim swap file"), mfp->mf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001311 goto theend;
1312 }
1313 if (b0_magic_wrong(b0p))
1314 {
1315 msg_start();
1316 msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
Bram Moolenaar48e330a2016-02-23 14:53:34 +01001317#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001318 if (STRNCMP(b0p->b0_hname, "PC ", 3) == 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001319 msg_puts_attr(_(" cannot be used with this version of Vim.\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001320 attr | MSG_HIST);
1321 else
1322#endif
Bram Moolenaar32526b32019-01-19 17:43:09 +01001323 msg_puts_attr(_(" cannot be used on this computer.\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001324 attr | MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001325 msg_puts_attr(_("The file was created on "), attr | MSG_HIST);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001326 // avoid going past the end of a corrupted hostname
Bram Moolenaar071d4272004-06-13 20:20:40 +00001327 b0p->b0_fname[0] = NUL;
Bram Moolenaar32526b32019-01-19 17:43:09 +01001328 msg_puts_attr((char *)b0p->b0_hname, attr | MSG_HIST);
1329 msg_puts_attr(_(",\nor the file has been damaged."), attr | MSG_HIST);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001330 msg_end();
1331 goto theend;
1332 }
Bram Moolenaar1c536282007-04-26 15:21:56 +00001333
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001334#ifdef FEAT_CRYPT
K.Takataeeec2542021-06-02 13:28:16 +02001335 for (i = 0; i < (int)ARRAY_LENGTH(id1_codes); ++i)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001336 if (id1_codes[i] == b0p->b0_id[1])
1337 b0_cm = i;
1338 if (b0_cm > 0)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001339 mch_memmove(mfp->mf_seed, &b0p->b0_seed, MF_SEED_LEN);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001340 crypt_set_cm_option(buf, b0_cm < 0 ? 0 : b0_cm);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001341#else
1342 if (b0p->b0_id[1] != BLOCK0_ID1)
1343 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001344 semsg(_("E833: %s is encrypted and this version of Vim does not support encryption"), mfp->mf_fname);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001345 goto theend;
1346 }
1347#endif
1348
Bram Moolenaar071d4272004-06-13 20:20:40 +00001349 /*
1350 * If we guessed the wrong page size, we have to recalculate the
1351 * highest block number in the file.
1352 */
1353 if (mfp->mf_page_size != (unsigned)char_to_long(b0p->b0_page_size))
1354 {
Bram Moolenaar1c536282007-04-26 15:21:56 +00001355 unsigned previous_page_size = mfp->mf_page_size;
1356
Bram Moolenaar071d4272004-06-13 20:20:40 +00001357 mf_new_page_size(mfp, (unsigned)char_to_long(b0p->b0_page_size));
Bram Moolenaar1c536282007-04-26 15:21:56 +00001358 if (mfp->mf_page_size < previous_page_size)
1359 {
1360 msg_start();
1361 msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001362 msg_puts_attr(_(" has been damaged (page size is smaller than minimum value).\n"),
Bram Moolenaar1c536282007-04-26 15:21:56 +00001363 attr | MSG_HIST);
1364 msg_end();
1365 goto theend;
1366 }
Bram Moolenaar8767f522016-07-01 17:17:39 +02001367 if ((size = vim_lseek(mfp->mf_fd, (off_T)0L, SEEK_END)) <= 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001368 mfp->mf_blocknr_max = 0; // no file or empty file
Bram Moolenaar071d4272004-06-13 20:20:40 +00001369 else
1370 mfp->mf_blocknr_max = (blocknr_T)(size / mfp->mf_page_size);
1371 mfp->mf_infile_count = mfp->mf_blocknr_max;
Bram Moolenaar1c536282007-04-26 15:21:56 +00001372
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001373 // need to reallocate the memory used to store the data
Bram Moolenaar1c536282007-04-26 15:21:56 +00001374 p = alloc(mfp->mf_page_size);
1375 if (p == NULL)
1376 goto theend;
1377 mch_memmove(p, hp->bh_data, previous_page_size);
1378 vim_free(hp->bh_data);
1379 hp->bh_data = p;
1380 b0p = (ZERO_BL *)(hp->bh_data);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001381 }
1382
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001383 /*
1384 * If .swp file name given directly, use name from swap file for buffer.
1385 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001386 if (directly)
1387 {
1388 expand_env(b0p->b0_fname, NameBuff, MAXPATHL);
1389 if (setfname(curbuf, NameBuff, NULL, TRUE) == FAIL)
1390 goto theend;
1391 }
1392
1393 home_replace(NULL, mfp->mf_fname, NameBuff, MAXPATHL, TRUE);
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001394 smsg(_("Using swap file \"%s\""), NameBuff);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001395
1396 if (buf_spname(curbuf) != NULL)
Bram Moolenaare1704ba2012-10-03 18:25:00 +02001397 vim_strncpy(NameBuff, buf_spname(curbuf), MAXPATHL - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001398 else
1399 home_replace(NULL, curbuf->b_ffname, NameBuff, MAXPATHL, TRUE);
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001400 smsg(_("Original file \"%s\""), NameBuff);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001401 msg_putchar('\n');
1402
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001403 /*
1404 * check date of swap file and original file
1405 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001406 mtime = char_to_long(b0p->b0_mtime);
1407 if (curbuf->b_ffname != NULL
1408 && mch_stat((char *)curbuf->b_ffname, &org_stat) != -1
1409 && ((mch_stat((char *)mfp->mf_fname, &swp_stat) != -1
1410 && org_stat.st_mtime > swp_stat.st_mtime)
1411 || org_stat.st_mtime != mtime))
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001412 emsg(_("E308: Warning: Original file may have been changed"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001413 out_flush();
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001414
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001415 // Get the 'fileformat' and 'fileencoding' from block zero.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001416 b0_ff = (b0p->b0_flags & B0_FF_MASK);
1417 if (b0p->b0_flags & B0_HAS_FENC)
1418 {
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001419 int fnsize = B0_FNAME_SIZE_NOCRYPT;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001420
1421#ifdef FEAT_CRYPT
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001422 // Use the same size as in add_b0_fenc().
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001423 if (b0p->b0_id[1] != BLOCK0_ID1)
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001424 fnsize = B0_FNAME_SIZE_CRYPT;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001425#endif
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001426 for (p = b0p->b0_fname + fnsize; p > b0p->b0_fname && p[-1] != NUL; --p)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001427 ;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001428 b0_fenc = vim_strnsave(p, b0p->b0_fname + fnsize - p);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001429 }
1430
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001431 mf_put(mfp, hp, FALSE, FALSE); // release block 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001432 hp = NULL;
1433
1434 /*
1435 * Now that we are sure that the file is going to be recovered, clear the
1436 * contents of the current buffer.
1437 */
1438 while (!(curbuf->b_ml.ml_flags & ML_EMPTY))
Bram Moolenaarca70c072020-05-30 20:30:46 +02001439 ml_delete((linenr_T)1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001440
1441 /*
1442 * Try reading the original file to obtain the values of 'fileformat',
1443 * 'fileencoding', etc. Ignore errors. The text itself is not used.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001444 * When the file is encrypted the user is asked to enter the key.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001445 */
1446 if (curbuf->b_ffname != NULL)
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001447 orig_file_status = readfile(curbuf->b_ffname, NULL, (linenr_T)0,
Bram Moolenaar071d4272004-06-13 20:20:40 +00001448 (linenr_T)0, (linenr_T)MAXLNUM, NULL, READ_NEW);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001449
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001450#ifdef FEAT_CRYPT
1451 if (b0_cm >= 0)
1452 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001453 // Need to ask the user for the crypt key. If this fails we continue
1454 // without a key, will probably get garbage text.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001455 if (*curbuf->b_p_key != NUL)
1456 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001457 smsg(_("Swap file is encrypted: \"%s\""), fname_used);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001458 msg_puts(_("\nIf you entered a new crypt key but did not write the text file,"));
1459 msg_puts(_("\nenter the new crypt key."));
1460 msg_puts(_("\nIf you wrote the text file after changing the crypt key press enter"));
1461 msg_puts(_("\nto use the same key for text file and swap file"));
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001462 }
1463 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001464 smsg(_(need_key_msg), fname_used);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001465 buf->b_p_key = crypt_get_key(FALSE, FALSE);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001466 if (buf->b_p_key == NULL)
1467 buf->b_p_key = curbuf->b_p_key;
1468 else if (*buf->b_p_key == NUL)
1469 {
1470 vim_free(buf->b_p_key);
1471 buf->b_p_key = curbuf->b_p_key;
1472 }
1473 if (buf->b_p_key == NULL)
1474 buf->b_p_key = empty_option;
1475 }
1476#endif
1477
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001478 // Use the 'fileformat' and 'fileencoding' as stored in the swap file.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001479 if (b0_ff != 0)
1480 set_fileformat(b0_ff - 1, OPT_LOCAL);
1481 if (b0_fenc != NULL)
1482 {
1483 set_option_value((char_u *)"fenc", 0L, b0_fenc, OPT_LOCAL);
1484 vim_free(b0_fenc);
1485 }
Bram Moolenaarc024b462019-06-08 18:07:21 +02001486 unchanged(curbuf, TRUE, TRUE);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001487
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001488 bnum = 1; // start with block 1
1489 page_count = 1; // which is 1 page
1490 lnum = 0; // append after line 0 in curbuf
Bram Moolenaar071d4272004-06-13 20:20:40 +00001491 line_count = 0;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001492 idx = 0; // start with first index in block 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00001493 error = 0;
1494 buf->b_ml.ml_stack_top = 0;
1495 buf->b_ml.ml_stack = NULL;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001496 buf->b_ml.ml_stack_size = 0; // no stack yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00001497
1498 if (curbuf->b_ffname == NULL)
1499 cannot_open = TRUE;
1500 else
1501 cannot_open = FALSE;
1502
1503 serious_error = FALSE;
1504 for ( ; !got_int; line_breakcheck())
1505 {
1506 if (hp != NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001507 mf_put(mfp, hp, FALSE, FALSE); // release previous block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001508
1509 /*
1510 * get block
1511 */
1512 if ((hp = mf_get(mfp, (blocknr_T)bnum, page_count)) == NULL)
1513 {
1514 if (bnum == 1)
1515 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001516 semsg(_("E309: Unable to read block 1 from %s"), mfp->mf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001517 goto theend;
1518 }
1519 ++error;
1520 ml_append(lnum++, (char_u *)_("???MANY LINES MISSING"),
1521 (colnr_T)0, TRUE);
1522 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001523 else // there is a block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001524 {
1525 pp = (PTR_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001526 if (pp->pb_id == PTR_ID) // it is a pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001527 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001528 // check line count when using pointer block first time
Bram Moolenaar071d4272004-06-13 20:20:40 +00001529 if (idx == 0 && line_count != 0)
1530 {
1531 for (i = 0; i < (int)pp->pb_count; ++i)
1532 line_count -= pp->pb_pointer[i].pe_line_count;
1533 if (line_count != 0)
1534 {
1535 ++error;
1536 ml_append(lnum++, (char_u *)_("???LINE COUNT WRONG"),
1537 (colnr_T)0, TRUE);
1538 }
1539 }
1540
1541 if (pp->pb_count == 0)
1542 {
1543 ml_append(lnum++, (char_u *)_("???EMPTY BLOCK"),
1544 (colnr_T)0, TRUE);
1545 ++error;
1546 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001547 else if (idx < (int)pp->pb_count) // go a block deeper
Bram Moolenaar071d4272004-06-13 20:20:40 +00001548 {
1549 if (pp->pb_pointer[idx].pe_bnum < 0)
1550 {
1551 /*
1552 * Data block with negative block number.
1553 * Try to read lines from the original file.
1554 * This is slow, but it works.
1555 */
1556 if (!cannot_open)
1557 {
1558 line_count = pp->pb_pointer[idx].pe_line_count;
1559 if (readfile(curbuf->b_ffname, NULL, lnum,
1560 pp->pb_pointer[idx].pe_old_lnum - 1,
Bram Moolenaare13b9af2017-01-13 22:01:02 +01001561 line_count, NULL, 0) != OK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001562 cannot_open = TRUE;
1563 else
1564 lnum += line_count;
1565 }
1566 if (cannot_open)
1567 {
1568 ++error;
1569 ml_append(lnum++, (char_u *)_("???LINES MISSING"),
1570 (colnr_T)0, TRUE);
1571 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001572 ++idx; // get same block again for next index
Bram Moolenaar071d4272004-06-13 20:20:40 +00001573 continue;
1574 }
1575
1576 /*
1577 * going one block deeper in the tree
1578 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001579 if ((top = ml_add_stack(buf)) < 0) // new entry in stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00001580 {
1581 ++error;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001582 break; // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00001583 }
1584 ip = &(buf->b_ml.ml_stack[top]);
1585 ip->ip_bnum = bnum;
1586 ip->ip_index = idx;
1587
1588 bnum = pp->pb_pointer[idx].pe_bnum;
1589 line_count = pp->pb_pointer[idx].pe_line_count;
1590 page_count = pp->pb_pointer[idx].pe_page_count;
Bram Moolenaar986a0032011-06-13 01:07:27 +02001591 idx = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001592 continue;
1593 }
1594 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001595 else // not a pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001596 {
1597 dp = (DATA_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001598 if (dp->db_id != DATA_ID) // block id wrong
Bram Moolenaar071d4272004-06-13 20:20:40 +00001599 {
1600 if (bnum == 1)
1601 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001602 semsg(_("E310: Block 1 ID wrong (%s not a .swp file?)"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001603 mfp->mf_fname);
1604 goto theend;
1605 }
1606 ++error;
1607 ml_append(lnum++, (char_u *)_("???BLOCK MISSING"),
1608 (colnr_T)0, TRUE);
1609 }
1610 else
1611 {
1612 /*
1613 * it is a data block
1614 * Append all the lines in this block
1615 */
1616 has_error = FALSE;
1617 /*
1618 * check length of block
1619 * if wrong, use length in pointer block
1620 */
1621 if (page_count * mfp->mf_page_size != dp->db_txt_end)
1622 {
1623 ml_append(lnum++, (char_u *)_("??? from here until ???END lines may be messed up"),
1624 (colnr_T)0, TRUE);
1625 ++error;
1626 has_error = TRUE;
1627 dp->db_txt_end = page_count * mfp->mf_page_size;
1628 }
1629
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001630 // make sure there is a NUL at the end of the block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001631 *((char_u *)dp + dp->db_txt_end - 1) = NUL;
1632
1633 /*
1634 * check number of lines in block
1635 * if wrong, use count in data block
1636 */
1637 if (line_count != dp->db_line_count)
1638 {
1639 ml_append(lnum++, (char_u *)_("??? from here until ???END lines may have been inserted/deleted"),
1640 (colnr_T)0, TRUE);
1641 ++error;
1642 has_error = TRUE;
1643 }
1644
1645 for (i = 0; i < dp->db_line_count; ++i)
1646 {
1647 txt_start = (dp->db_index[i] & DB_INDEX_MASK);
Bram Moolenaar740885b2009-11-03 14:33:17 +00001648 if (txt_start <= (int)HEADER_SIZE
Bram Moolenaar071d4272004-06-13 20:20:40 +00001649 || txt_start >= (int)dp->db_txt_end)
1650 {
1651 p = (char_u *)"???";
1652 ++error;
1653 }
1654 else
1655 p = (char_u *)dp + txt_start;
1656 ml_append(lnum++, p, (colnr_T)0, TRUE);
1657 }
1658 if (has_error)
Bram Moolenaar740885b2009-11-03 14:33:17 +00001659 ml_append(lnum++, (char_u *)_("???END"),
1660 (colnr_T)0, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001661 }
1662 }
1663 }
1664
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001665 if (buf->b_ml.ml_stack_top == 0) // finished
Bram Moolenaar071d4272004-06-13 20:20:40 +00001666 break;
1667
1668 /*
1669 * go one block up in the tree
1670 */
1671 ip = &(buf->b_ml.ml_stack[--(buf->b_ml.ml_stack_top)]);
1672 bnum = ip->ip_bnum;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001673 idx = ip->ip_index + 1; // go to next index
Bram Moolenaar071d4272004-06-13 20:20:40 +00001674 page_count = 1;
1675 }
1676
1677 /*
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001678 * Compare the buffer contents with the original file. When they differ
1679 * set the 'modified' flag.
1680 * Lines 1 - lnum are the new contents.
1681 * Lines lnum + 1 to ml_line_count are the original contents.
1682 * Line ml_line_count + 1 in the dummy empty line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001683 */
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001684 if (orig_file_status != OK || curbuf->b_ml.ml_line_count != lnum * 2 + 1)
1685 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001686 // Recovering an empty file results in two lines and the first line is
1687 // empty. Don't set the modified flag then.
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001688 if (!(curbuf->b_ml.ml_line_count == 2 && *ml_get(1) == NUL))
1689 {
Bram Moolenaarec28d152019-05-11 18:36:34 +02001690 changed_internal();
Bram Moolenaar95c526e2017-02-25 14:59:34 +01001691 ++CHANGEDTICK(curbuf);
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001692 }
1693 }
1694 else
1695 {
1696 for (idx = 1; idx <= lnum; ++idx)
1697 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001698 // Need to copy one line, fetching the other one may flush it.
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001699 p = vim_strsave(ml_get(idx));
1700 i = STRCMP(p, ml_get(idx + lnum));
1701 vim_free(p);
1702 if (i != 0)
1703 {
Bram Moolenaarec28d152019-05-11 18:36:34 +02001704 changed_internal();
Bram Moolenaar95c526e2017-02-25 14:59:34 +01001705 ++CHANGEDTICK(curbuf);
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001706 break;
1707 }
1708 }
1709 }
1710
1711 /*
1712 * Delete the lines from the original file and the dummy line from the
1713 * empty buffer. These will now be after the last line in the buffer.
1714 */
1715 while (curbuf->b_ml.ml_line_count > lnum
1716 && !(curbuf->b_ml.ml_flags & ML_EMPTY))
Bram Moolenaarca70c072020-05-30 20:30:46 +02001717 ml_delete(curbuf->b_ml.ml_line_count);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001718 curbuf->b_flags |= BF_RECOVERED;
Bram Moolenaare3f50ad2021-06-09 12:33:40 +02001719 check_cursor();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001720
1721 recoverymode = FALSE;
1722 if (got_int)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001723 emsg(_("E311: Recovery Interrupted"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001724 else if (error)
1725 {
1726 ++no_wait_return;
Bram Moolenaar32526b32019-01-19 17:43:09 +01001727 msg(">>>>>>>>>>>>>");
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001728 emsg(_("E312: Errors detected while recovering; look for lines starting with ???"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001729 --no_wait_return;
Bram Moolenaar32526b32019-01-19 17:43:09 +01001730 msg(_("See \":help E312\" for more information."));
1731 msg(">>>>>>>>>>>>>");
Bram Moolenaar071d4272004-06-13 20:20:40 +00001732 }
1733 else
1734 {
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001735 if (curbuf->b_changed)
1736 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001737 msg(_("Recovery completed. You should check if everything is OK."));
1738 msg_puts(_("\n(You might want to write out this file under another name\n"));
1739 msg_puts(_("and run diff with the original file to check for changes)"));
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001740 }
1741 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001742 msg(_("Recovery completed. Buffer contents equals file contents."));
Bram Moolenaarf8835082020-11-09 21:04:17 +01001743 msg_puts(_("\nYou may want to delete the .swp file now."));
1744#if defined(UNIX) || defined(MSWIN)
Bram Moolenaarf52f0602021-03-10 21:26:37 +01001745 if (swapfile_process_running(b0p, fname_used))
Bram Moolenaarf8835082020-11-09 21:04:17 +01001746 {
1747 // Warn there could be an active Vim on the same file, the user may
1748 // want to kill it.
1749 msg_puts(_("\nNote: process STILL RUNNING: "));
1750 msg_outnum(char_to_long(b0p->b0_pid));
1751 }
1752#endif
1753 msg_puts("\n\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00001754 cmdline_row = msg_row;
1755 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001756#ifdef FEAT_CRYPT
1757 if (*buf->b_p_key != NUL && STRCMP(curbuf->b_p_key, buf->b_p_key) != 0)
1758 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001759 msg_puts(_("Using crypt key from swap file for the text file.\n"));
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001760 set_option_value((char_u *)"key", 0L, buf->b_p_key, OPT_LOCAL);
1761 }
1762#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001763 redraw_curbuf_later(NOT_VALID);
1764
1765theend:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001766 vim_free(fname_used);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001767 recoverymode = FALSE;
1768 if (mfp != NULL)
1769 {
1770 if (hp != NULL)
1771 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001772 mf_close(mfp, FALSE); // will also vim_free(mfp->mf_fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001773 }
Bram Moolenaardf88dda2007-01-09 13:34:50 +00001774 if (buf != NULL)
1775 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001776#ifdef FEAT_CRYPT
1777 if (buf->b_p_key != curbuf->b_p_key)
1778 free_string_option(buf->b_p_key);
Bram Moolenaar0ad014c2010-07-25 14:00:46 +02001779 free_string_option(buf->b_p_cm);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001780#endif
Bram Moolenaardf88dda2007-01-09 13:34:50 +00001781 vim_free(buf->b_ml.ml_stack);
1782 vim_free(buf);
1783 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001784 if (serious_error && called_from_main)
1785 ml_close(curbuf, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001786 else
1787 {
1788 apply_autocmds(EVENT_BUFREADPOST, NULL, curbuf->b_fname, FALSE, curbuf);
1789 apply_autocmds(EVENT_BUFWINENTER, NULL, curbuf->b_fname, FALSE, curbuf);
1790 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001791 return;
1792}
1793
1794/*
1795 * Find the names of swap files in current directory and the directory given
1796 * with the 'directory' option.
1797 *
1798 * Used to:
1799 * - list the swap files for "vim -r"
1800 * - count the number of swap files when recovering
1801 * - list the swap files when recovering
1802 * - find the name of the n'th swap file when recovering
1803 */
1804 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001805recover_names(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001806 char_u *fname, // base for swap file name
1807 int list, // when TRUE, list the swap file names
1808 int nr, // when non-zero, return nr'th swap file name
1809 char_u **fname_out) // result when "nr" > 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001810{
1811 int num_names;
1812 char_u *(names[6]);
1813 char_u *tail;
1814 char_u *p;
1815 int num_files;
1816 int file_count = 0;
1817 char_u **files;
1818 int i;
1819 char_u *dirp;
1820 char_u *dir_name;
Bram Moolenaar64354da2010-05-25 21:37:17 +02001821 char_u *fname_res = NULL;
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001822#ifdef HAVE_READLINK
1823 char_u fname_buf[MAXPATHL];
Bram Moolenaar64354da2010-05-25 21:37:17 +02001824#endif
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001825
Bram Moolenaar64354da2010-05-25 21:37:17 +02001826 if (fname != NULL)
1827 {
1828#ifdef HAVE_READLINK
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001829 // Expand symlink in the file name, because the swap file is created
1830 // with the actual file instead of with the symlink.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001831 if (resolve_symlink(fname, fname_buf) == OK)
1832 fname_res = fname_buf;
1833 else
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001834#endif
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001835 fname_res = fname;
Bram Moolenaar64354da2010-05-25 21:37:17 +02001836 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001837
1838 if (list)
1839 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001840 // use msg() to start the scrolling properly
Bram Moolenaar32526b32019-01-19 17:43:09 +01001841 msg(_("Swap files found:"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001842 msg_putchar('\n');
1843 }
1844
1845 /*
1846 * Do the loop for every directory in 'directory'.
1847 * First allocate some memory to put the directory name in.
1848 */
Bram Moolenaar964b3742019-05-24 18:54:09 +02001849 dir_name = alloc(STRLEN(p_dir) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001850 dirp = p_dir;
1851 while (dir_name != NULL && *dirp)
1852 {
1853 /*
1854 * Isolate a directory name from *dirp and put it in dir_name (we know
1855 * it is large enough, so use 31000 for length).
1856 * Advance dirp to next directory name.
1857 */
1858 (void)copy_option_part(&dirp, dir_name, 31000, ",");
1859
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001860 if (dir_name[0] == '.' && dir_name[1] == NUL) // check current dir
Bram Moolenaar071d4272004-06-13 20:20:40 +00001861 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001862 if (fname == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001863 {
1864#ifdef VMS
1865 names[0] = vim_strsave((char_u *)"*_sw%");
1866#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001867 names[0] = vim_strsave((char_u *)"*.sw?");
Bram Moolenaar071d4272004-06-13 20:20:40 +00001868#endif
Bram Moolenaar4f974752019-02-17 17:44:42 +01001869#if defined(UNIX) || defined(MSWIN)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001870 // For Unix names starting with a dot are special. MS-Windows
1871 // supports this too, on some file systems.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001872 names[1] = vim_strsave((char_u *)".*.sw?");
1873 names[2] = vim_strsave((char_u *)".sw?");
1874 num_names = 3;
1875#else
1876# ifdef VMS
1877 names[1] = vim_strsave((char_u *)".*_sw%");
1878 num_names = 2;
1879# else
1880 num_names = 1;
1881# endif
1882#endif
1883 }
1884 else
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001885 num_names = recov_file_names(names, fname_res, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001886 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001887 else // check directory dir_name
Bram Moolenaar071d4272004-06-13 20:20:40 +00001888 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001889 if (fname == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001890 {
1891#ifdef VMS
1892 names[0] = concat_fnames(dir_name, (char_u *)"*_sw%", TRUE);
1893#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001894 names[0] = concat_fnames(dir_name, (char_u *)"*.sw?", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001895#endif
Bram Moolenaar4f974752019-02-17 17:44:42 +01001896#if defined(UNIX) || defined(MSWIN)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001897 // For Unix names starting with a dot are special. MS-Windows
1898 // supports this too, on some file systems.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001899 names[1] = concat_fnames(dir_name, (char_u *)".*.sw?", TRUE);
1900 names[2] = concat_fnames(dir_name, (char_u *)".sw?", TRUE);
1901 num_names = 3;
1902#else
1903# ifdef VMS
1904 names[1] = concat_fnames(dir_name, (char_u *)".*_sw%", TRUE);
1905 num_names = 2;
1906# else
1907 num_names = 1;
1908# endif
1909#endif
1910 }
1911 else
1912 {
Bram Moolenaar4f974752019-02-17 17:44:42 +01001913#if defined(UNIX) || defined(MSWIN)
Bram Moolenaarb113c3a2017-02-28 21:26:17 +01001914 int len = (int)STRLEN(dir_name);
Bram Moolenaarc525e3a2017-02-18 16:59:02 +01001915
1916 p = dir_name + len;
1917 if (after_pathsep(dir_name, p) && len > 1 && p[-1] == p[-2])
Bram Moolenaar071d4272004-06-13 20:20:40 +00001918 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001919 // Ends with '//', Use Full path for swap name
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001920 tail = make_percent_swname(dir_name, fname_res);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001921 }
1922 else
1923#endif
1924 {
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001925 tail = gettail(fname_res);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001926 tail = concat_fnames(dir_name, tail, TRUE);
1927 }
1928 if (tail == NULL)
1929 num_names = 0;
1930 else
1931 {
1932 num_names = recov_file_names(names, tail, FALSE);
1933 vim_free(tail);
1934 }
1935 }
1936 }
1937
Bram Moolenaar0d3cb732019-05-18 13:05:18 +02001938 // check for out-of-memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00001939 for (i = 0; i < num_names; ++i)
1940 {
1941 if (names[i] == NULL)
1942 {
1943 for (i = 0; i < num_names; ++i)
1944 vim_free(names[i]);
1945 num_names = 0;
1946 }
1947 }
1948 if (num_names == 0)
1949 num_files = 0;
1950 else if (expand_wildcards(num_names, names, &num_files, &files,
Bram Moolenaar99499b12019-05-23 21:35:48 +02001951 EW_NOTENV|EW_KEEPALL|EW_FILE|EW_SILENT) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001952 num_files = 0;
1953
1954 /*
1955 * When no swap file found, wildcard expansion might have failed (e.g.
1956 * not able to execute the shell).
1957 * Try finding a swap file by simply adding ".swp" to the file name.
1958 */
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001959 if (*dirp == NUL && file_count + num_files == 0 && fname != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001960 {
Bram Moolenaar8767f522016-07-01 17:17:39 +02001961 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001962 char_u *swapname;
1963
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001964 swapname = modname(fname_res,
Bram Moolenaare60acc12011-05-10 16:41:25 +02001965#if defined(VMS)
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001966 (char_u *)"_swp", FALSE
Bram Moolenaar071d4272004-06-13 20:20:40 +00001967#else
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001968 (char_u *)".swp", TRUE
Bram Moolenaar071d4272004-06-13 20:20:40 +00001969#endif
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001970 );
Bram Moolenaar071d4272004-06-13 20:20:40 +00001971 if (swapname != NULL)
1972 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001973 if (mch_stat((char *)swapname, &st) != -1) // It exists!
Bram Moolenaar071d4272004-06-13 20:20:40 +00001974 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001975 files = ALLOC_ONE(char_u *);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001976 if (files != NULL)
1977 {
1978 files[0] = swapname;
1979 swapname = NULL;
1980 num_files = 1;
1981 }
1982 }
1983 vim_free(swapname);
1984 }
1985 }
1986
1987 /*
1988 * remove swapfile name of the current buffer, it must be ignored
1989 */
1990 if (curbuf->b_ml.ml_mfp != NULL
1991 && (p = curbuf->b_ml.ml_mfp->mf_fname) != NULL)
1992 {
1993 for (i = 0; i < num_files; ++i)
Bram Moolenaar99499b12019-05-23 21:35:48 +02001994 // Do not expand wildcards, on windows would try to expand
1995 // "%tmp%" in "%tmp%file".
1996 if (fullpathcmp(p, files[i], TRUE, FALSE) & FPC_SAME)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001997 {
Bram Moolenaar99499b12019-05-23 21:35:48 +02001998 // Remove the name from files[i]. Move further entries
1999 // down. When the array becomes empty free it here, since
2000 // FreeWild() won't be called below.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002001 vim_free(files[i]);
Bram Moolenaar9439cdd2009-04-22 13:39:36 +00002002 if (--num_files == 0)
2003 vim_free(files);
2004 else
2005 for ( ; i < num_files; ++i)
2006 files[i] = files[i + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002007 }
2008 }
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00002009 if (nr > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002010 {
2011 file_count += num_files;
2012 if (nr <= file_count)
2013 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002014 *fname_out = vim_strsave(
2015 files[nr - 1 + num_files - file_count]);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002016 dirp = (char_u *)""; // stop searching
Bram Moolenaar071d4272004-06-13 20:20:40 +00002017 }
2018 }
2019 else if (list)
2020 {
2021 if (dir_name[0] == '.' && dir_name[1] == NUL)
2022 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002023 if (fname == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002024 msg_puts(_(" In current directory:\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002025 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002026 msg_puts(_(" Using specified name:\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002027 }
2028 else
2029 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002030 msg_puts(_(" In directory "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002031 msg_home_replace(dir_name);
Bram Moolenaar32526b32019-01-19 17:43:09 +01002032 msg_puts(":\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00002033 }
2034
2035 if (num_files)
2036 {
2037 for (i = 0; i < num_files; ++i)
2038 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002039 // print the swap file name
Bram Moolenaar071d4272004-06-13 20:20:40 +00002040 msg_outnum((long)++file_count);
Bram Moolenaar32526b32019-01-19 17:43:09 +01002041 msg_puts(". ");
2042 msg_puts((char *)gettail(files[i]));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002043 msg_putchar('\n');
2044 (void)swapfile_info(files[i]);
2045 }
2046 }
2047 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002048 msg_puts(_(" -- none --\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002049 out_flush();
2050 }
2051 else
2052 file_count += num_files;
2053
2054 for (i = 0; i < num_names; ++i)
2055 vim_free(names[i]);
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00002056 if (num_files > 0)
2057 FreeWild(num_files, files);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002058 }
2059 vim_free(dir_name);
2060 return file_count;
2061}
2062
Bram Moolenaar4f974752019-02-17 17:44:42 +01002063#if defined(UNIX) || defined(MSWIN) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002064/*
Bram Moolenaarb782ba42018-08-07 21:39:28 +02002065 * Need _very_ long file names.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002066 * Append the full path to name with path separators made into percent
2067 * signs, to dir. An unnamed buffer is handled as "" (<currentdir>/"")
2068 */
Bram Moolenaarb782ba42018-08-07 21:39:28 +02002069 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002070make_percent_swname(char_u *dir, char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002071{
Bram Moolenaarb782ba42018-08-07 21:39:28 +02002072 char_u *d = NULL, *s, *f;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002073
Bram Moolenaarb782ba42018-08-07 21:39:28 +02002074 f = fix_fname(name != NULL ? name : (char_u *)"");
Bram Moolenaar071d4272004-06-13 20:20:40 +00002075 if (f != NULL)
2076 {
Bram Moolenaar964b3742019-05-24 18:54:09 +02002077 s = alloc(STRLEN(f) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002078 if (s != NULL)
2079 {
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002080 STRCPY(s, f);
Bram Moolenaar91acfff2017-03-12 19:22:36 +01002081 for (d = s; *d != NUL; MB_PTR_ADV(d))
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002082 if (vim_ispathsep(*d))
2083 *d = '%';
Bram Moolenaar071d4272004-06-13 20:20:40 +00002084 d = concat_fnames(dir, s, TRUE);
2085 vim_free(s);
2086 }
2087 vim_free(f);
2088 }
2089 return d;
2090}
2091#endif
2092
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02002093#if (defined(UNIX) || defined(VMS) || defined(MSWIN)) \
2094 && (defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG))
2095# define HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar071d4272004-06-13 20:20:40 +00002096static int process_still_running;
2097#endif
2098
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002099#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002100/*
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002101 * Return information found in swapfile "fname" in dictionary "d".
2102 * This is used by the swapinfo() function.
2103 */
2104 void
2105get_b0_dict(char_u *fname, dict_T *d)
2106{
2107 int fd;
2108 struct block0 b0;
2109
2110 if ((fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0)) >= 0)
2111 {
2112 if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0))
2113 {
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002114 if (ml_check_b0_id(&b0) == FAIL)
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002115 dict_add_string(d, "error", (char_u *)"Not a swap file");
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002116 else if (b0_magic_wrong(&b0))
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002117 dict_add_string(d, "error", (char_u *)"Magic number mismatch");
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002118 else
2119 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002120 // we have swap information
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002121 dict_add_string_len(d, "version", b0.b0_version, 10);
2122 dict_add_string_len(d, "user", b0.b0_uname, B0_UNAME_SIZE);
2123 dict_add_string_len(d, "host", b0.b0_hname, B0_HNAME_SIZE);
2124 dict_add_string_len(d, "fname", b0.b0_fname, B0_FNAME_SIZE_ORG);
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002125
2126 dict_add_number(d, "pid", char_to_long(b0.b0_pid));
2127 dict_add_number(d, "mtime", char_to_long(b0.b0_mtime));
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002128 dict_add_number(d, "dirty", b0.b0_dirty ? 1 : 0);
2129# ifdef CHECK_INODE
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002130 dict_add_number(d, "inode", char_to_long(b0.b0_ino));
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002131# endif
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002132 }
2133 }
2134 else
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002135 dict_add_string(d, "error", (char_u *)"Cannot read file");
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002136 close(fd);
2137 }
2138 else
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002139 dict_add_string(d, "error", (char_u *)"Cannot open file");
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002140}
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002141#endif
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002142
2143/*
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00002144 * Give information about an existing swap file.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002145 * Returns timestamp (0 when unknown).
2146 */
2147 static time_t
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002148swapfile_info(char_u *fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002149{
Bram Moolenaar8767f522016-07-01 17:17:39 +02002150 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002151 int fd;
2152 struct block0 b0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002153#ifdef UNIX
2154 char_u uname[B0_UNAME_SIZE];
2155#endif
2156
Bram Moolenaar63d25552019-05-10 21:28:38 +02002157 // print the swap file date
Bram Moolenaar071d4272004-06-13 20:20:40 +00002158 if (mch_stat((char *)fname, &st) != -1)
2159 {
2160#ifdef UNIX
Bram Moolenaar63d25552019-05-10 21:28:38 +02002161 // print name of owner of the file
Bram Moolenaar071d4272004-06-13 20:20:40 +00002162 if (mch_get_uname(st.st_uid, uname, B0_UNAME_SIZE) == OK)
2163 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002164 msg_puts(_(" owned by: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002165 msg_outtrans(uname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01002166 msg_puts(_(" dated: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002167 }
2168 else
2169#endif
Bram Moolenaar32526b32019-01-19 17:43:09 +01002170 msg_puts(_(" dated: "));
Bram Moolenaar63d25552019-05-10 21:28:38 +02002171 msg_puts(get_ctime(st.st_mtime, TRUE));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002172 }
Bram Moolenaar63d25552019-05-10 21:28:38 +02002173 else
2174 st.st_mtime = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002175
2176 /*
2177 * print the original file name
2178 */
2179 fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
2180 if (fd >= 0)
2181 {
Bram Moolenaar540fc6f2010-12-17 16:27:16 +01002182 if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002183 {
2184 if (STRNCMP(b0.b0_version, "VIM 3.0", 7) == 0)
2185 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002186 msg_puts(_(" [from Vim version 3.0]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002187 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002188 else if (ml_check_b0_id(&b0) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002189 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002190 msg_puts(_(" [does not look like a Vim swap file]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002191 }
2192 else
2193 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002194 msg_puts(_(" file name: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002195 if (b0.b0_fname[0] == NUL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002196 msg_puts(_("[No Name]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002197 else
2198 msg_outtrans(b0.b0_fname);
2199
Bram Moolenaar32526b32019-01-19 17:43:09 +01002200 msg_puts(_("\n modified: "));
2201 msg_puts(b0.b0_dirty ? _("YES") : _("no"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002202
2203 if (*(b0.b0_uname) != NUL)
2204 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002205 msg_puts(_("\n user name: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002206 msg_outtrans(b0.b0_uname);
2207 }
2208
2209 if (*(b0.b0_hname) != NUL)
2210 {
2211 if (*(b0.b0_uname) != NUL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002212 msg_puts(_(" host name: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002213 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002214 msg_puts(_("\n host name: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002215 msg_outtrans(b0.b0_hname);
2216 }
2217
2218 if (char_to_long(b0.b0_pid) != 0L)
2219 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002220 msg_puts(_("\n process ID: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002221 msg_outnum(char_to_long(b0.b0_pid));
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002222#if defined(UNIX) || defined(MSWIN)
Bram Moolenaarf52f0602021-03-10 21:26:37 +01002223 if (swapfile_process_running(&b0, fname))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002224 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002225 msg_puts(_(" (STILL RUNNING)"));
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02002226# ifdef HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar071d4272004-06-13 20:20:40 +00002227 process_still_running = TRUE;
2228# endif
2229 }
2230#endif
2231 }
2232
2233 if (b0_magic_wrong(&b0))
2234 {
Bram Moolenaar48e330a2016-02-23 14:53:34 +01002235#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002236 if (STRNCMP(b0.b0_hname, "PC ", 3) == 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002237 msg_puts(_("\n [not usable with this version of Vim]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002238 else
2239#endif
Bram Moolenaar32526b32019-01-19 17:43:09 +01002240 msg_puts(_("\n [not usable on this computer]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002241 }
2242 }
2243 }
2244 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002245 msg_puts(_(" [cannot be read]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002246 close(fd);
2247 }
2248 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002249 msg_puts(_(" [cannot be opened]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002250 msg_putchar('\n');
2251
Bram Moolenaar63d25552019-05-10 21:28:38 +02002252 return st.st_mtime;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002253}
2254
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002255/*
2256 * Return TRUE if the swap file looks OK and there are no changes, thus it can
2257 * be safely deleted.
2258 */
2259 static time_t
2260swapfile_unchanged(char_u *fname)
2261{
2262 stat_T st;
2263 int fd;
2264 struct block0 b0;
2265 int ret = TRUE;
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002266
2267 // must be able to stat the swap file
2268 if (mch_stat((char *)fname, &st) == -1)
2269 return FALSE;
2270
2271 // must be able to read the first block
2272 fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
2273 if (fd < 0)
2274 return FALSE;
2275 if (read_eintr(fd, &b0, sizeof(b0)) != sizeof(b0))
2276 {
2277 close(fd);
2278 return FALSE;
2279 }
2280
2281 // the ID and magic number must be correct
2282 if (ml_check_b0_id(&b0) == FAIL|| b0_magic_wrong(&b0))
2283 ret = FALSE;
2284
2285 // must be unchanged
2286 if (b0.b0_dirty)
2287 ret = FALSE;
2288
2289#if defined(UNIX) || defined(MSWIN)
Bram Moolenaarf8835082020-11-09 21:04:17 +01002290 // Host name must be known and must equal the current host name, otherwise
2291 // comparing pid is meaningless.
2292 if (*(b0.b0_hname) == NUL)
2293 {
2294 ret = FALSE;
2295 }
2296 else
2297 {
2298 char_u hostname[B0_HNAME_SIZE];
2299
2300 mch_get_host_name(hostname, B0_HNAME_SIZE);
2301 hostname[B0_HNAME_SIZE - 1] = NUL;
Bram Moolenaare79cdb62020-11-21 13:51:16 +01002302 b0.b0_hname[B0_HNAME_SIZE - 1] = NUL; // in case of corruption
Bram Moolenaarf8835082020-11-09 21:04:17 +01002303 if (STRICMP(b0.b0_hname, hostname) != 0)
2304 ret = FALSE;
2305 }
2306
Bram Moolenaar32aa1022019-11-02 22:54:41 +01002307 // process must be known and not be running
Bram Moolenaarf52f0602021-03-10 21:26:37 +01002308 if (char_to_long(b0.b0_pid) == 0L || swapfile_process_running(&b0, fname))
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002309 ret = FALSE;
2310#endif
2311
Bram Moolenaarf8835082020-11-09 21:04:17 +01002312 // We do not check the user, it should be irrelevant for whether the swap
2313 // file is still useful.
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002314
2315 close(fd);
2316 return ret;
2317}
2318
Bram Moolenaar071d4272004-06-13 20:20:40 +00002319 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002320recov_file_names(char_u **names, char_u *path, int prepend_dot)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002321{
2322 int num_names;
2323
Bram Moolenaar071d4272004-06-13 20:20:40 +00002324 /*
2325 * (Win32 and Win64) never short names, but do prepend a dot.
2326 * (Not MS-DOS or Win32 or Win64) maybe short name, maybe not: Try both.
2327 * Only use the short name if it is different.
2328 */
2329 char_u *p;
2330 int i;
Bram Moolenaar4f974752019-02-17 17:44:42 +01002331# ifndef MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002332 int shortname = curbuf->b_shortname;
2333
2334 curbuf->b_shortname = FALSE;
2335# endif
2336
2337 num_names = 0;
2338
2339 /*
2340 * May also add the file name with a dot prepended, for swap file in same
2341 * dir as original file.
2342 */
2343 if (prepend_dot)
2344 {
2345 names[num_names] = modname(path, (char_u *)".sw?", TRUE);
2346 if (names[num_names] == NULL)
2347 goto end;
2348 ++num_names;
2349 }
2350
2351 /*
2352 * Form the normal swap file name pattern by appending ".sw?".
2353 */
2354#ifdef VMS
2355 names[num_names] = concat_fnames(path, (char_u *)"_sw%", FALSE);
2356#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002357 names[num_names] = concat_fnames(path, (char_u *)".sw?", FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002358#endif
2359 if (names[num_names] == NULL)
2360 goto end;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002361 if (num_names >= 1) // check if we have the same name twice
Bram Moolenaar071d4272004-06-13 20:20:40 +00002362 {
2363 p = names[num_names - 1];
2364 i = (int)STRLEN(names[num_names - 1]) - (int)STRLEN(names[num_names]);
2365 if (i > 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002366 p += i; // file name has been expanded to full path
Bram Moolenaar071d4272004-06-13 20:20:40 +00002367
2368 if (STRCMP(p, names[num_names]) != 0)
2369 ++num_names;
2370 else
2371 vim_free(names[num_names]);
2372 }
2373 else
2374 ++num_names;
2375
Bram Moolenaar4f974752019-02-17 17:44:42 +01002376# ifndef MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002377 /*
2378 * Also try with 'shortname' set, in case the file is on a DOS filesystem.
2379 */
2380 curbuf->b_shortname = TRUE;
2381#ifdef VMS
2382 names[num_names] = modname(path, (char_u *)"_sw%", FALSE);
2383#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002384 names[num_names] = modname(path, (char_u *)".sw?", FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002385#endif
2386 if (names[num_names] == NULL)
2387 goto end;
2388
2389 /*
2390 * Remove the one from 'shortname', if it's the same as with 'noshortname'.
2391 */
2392 p = names[num_names];
2393 i = STRLEN(names[num_names]) - STRLEN(names[num_names - 1]);
2394 if (i > 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002395 p += i; // file name has been expanded to full path
Bram Moolenaar071d4272004-06-13 20:20:40 +00002396 if (STRCMP(names[num_names - 1], p) == 0)
2397 vim_free(names[num_names]);
2398 else
2399 ++num_names;
2400# endif
2401
2402end:
Bram Moolenaar4f974752019-02-17 17:44:42 +01002403# ifndef MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002404 curbuf->b_shortname = shortname;
2405# endif
2406
Bram Moolenaar071d4272004-06-13 20:20:40 +00002407 return num_names;
2408}
2409
2410/*
2411 * sync all memlines
2412 *
2413 * If 'check_file' is TRUE, check if original file exists and was not changed.
2414 * If 'check_char' is TRUE, stop syncing when character becomes available, but
2415 * always sync at least one block.
2416 */
2417 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002418ml_sync_all(int check_file, int check_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002419{
2420 buf_T *buf;
Bram Moolenaar8767f522016-07-01 17:17:39 +02002421 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002422
Bram Moolenaar29323592016-07-24 22:04:11 +02002423 FOR_ALL_BUFFERS(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002424 {
Christian Brabandtf573c6e2021-06-20 14:02:16 +02002425 if (buf->b_ml.ml_mfp == NULL
2426 || buf->b_ml.ml_mfp->mf_fname == NULL
2427 || buf->b_ml.ml_mfp->mf_fd < 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002428 continue; // no file
Bram Moolenaar071d4272004-06-13 20:20:40 +00002429
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002430 ml_flush_line(buf); // flush buffered line
2431 // flush locked block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002432 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH);
2433 if (bufIsChanged(buf) && check_file && mf_need_trans(buf->b_ml.ml_mfp)
2434 && buf->b_ffname != NULL)
2435 {
2436 /*
2437 * If the original file does not exist anymore or has been changed
2438 * call ml_preserve() to get rid of all negative numbered blocks.
2439 */
2440 if (mch_stat((char *)buf->b_ffname, &st) == -1
2441 || st.st_mtime != buf->b_mtime_read
Bram Moolenaar914703b2010-05-31 21:59:46 +02002442 || st.st_size != buf->b_orig_size)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002443 {
2444 ml_preserve(buf, FALSE);
2445 did_check_timestamps = FALSE;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002446 need_check_timestamps = TRUE; // give message later
Bram Moolenaar071d4272004-06-13 20:20:40 +00002447 }
2448 }
2449 if (buf->b_ml.ml_mfp->mf_dirty)
2450 {
2451 (void)mf_sync(buf->b_ml.ml_mfp, (check_char ? MFS_STOP : 0)
2452 | (bufIsChanged(buf) ? MFS_FLUSH : 0));
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002453 if (check_char && ui_char_avail()) // character available now
Bram Moolenaar071d4272004-06-13 20:20:40 +00002454 break;
2455 }
2456 }
2457}
2458
2459/*
2460 * sync one buffer, including negative blocks
2461 *
2462 * after this all the blocks are in the swap file
2463 *
2464 * Used for the :preserve command and when the original file has been
2465 * changed or deleted.
2466 *
2467 * when message is TRUE the success of preserving is reported
2468 */
2469 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002470ml_preserve(buf_T *buf, int message)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002471{
2472 bhdr_T *hp;
2473 linenr_T lnum;
2474 memfile_T *mfp = buf->b_ml.ml_mfp;
2475 int status;
2476 int got_int_save = got_int;
2477
2478 if (mfp == NULL || mfp->mf_fname == NULL)
2479 {
2480 if (message)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002481 emsg(_("E313: Cannot preserve, there is no swap file"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002482 return;
2483 }
2484
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002485 // We only want to stop when interrupted here, not when interrupted
2486 // before.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002487 got_int = FALSE;
2488
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002489 ml_flush_line(buf); // flush buffered line
2490 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush locked block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002491 status = mf_sync(mfp, MFS_ALL | MFS_FLUSH);
2492
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002493 // stack is invalid after mf_sync(.., MFS_ALL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002494 buf->b_ml.ml_stack_top = 0;
2495
2496 /*
2497 * Some of the data blocks may have been changed from negative to
2498 * positive block number. In that case the pointer blocks need to be
2499 * updated.
2500 *
2501 * We don't know in which pointer block the references are, so we visit
2502 * all data blocks until there are no more translations to be done (or
2503 * we hit the end of the file, which can only happen in case a write fails,
2504 * e.g. when file system if full).
2505 * ml_find_line() does the work by translating the negative block numbers
2506 * when getting the first line of each data block.
2507 */
2508 if (mf_need_trans(mfp) && !got_int)
2509 {
2510 lnum = 1;
2511 while (mf_need_trans(mfp) && lnum <= buf->b_ml.ml_line_count)
2512 {
2513 hp = ml_find_line(buf, lnum, ML_FIND);
2514 if (hp == NULL)
2515 {
2516 status = FAIL;
2517 goto theend;
2518 }
2519 CHECK(buf->b_ml.ml_locked_low != lnum, "low != lnum");
2520 lnum = buf->b_ml.ml_locked_high + 1;
2521 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002522 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush locked block
2523 // sync the updated pointer blocks
Bram Moolenaar071d4272004-06-13 20:20:40 +00002524 if (mf_sync(mfp, MFS_ALL | MFS_FLUSH) == FAIL)
2525 status = FAIL;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002526 buf->b_ml.ml_stack_top = 0; // stack is invalid now
Bram Moolenaar071d4272004-06-13 20:20:40 +00002527 }
2528theend:
2529 got_int |= got_int_save;
2530
2531 if (message)
2532 {
2533 if (status == OK)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002534 msg(_("File preserved"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002535 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002536 emsg(_("E314: Preserve failed"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002537 }
2538}
2539
2540/*
2541 * NOTE: The pointer returned by the ml_get_*() functions only remains valid
2542 * until the next call!
2543 * line1 = ml_get(1);
2544 * line2 = ml_get(2); // line1 is now invalid!
2545 * Make a copy of the line if necessary.
2546 */
2547/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002548 * Return a pointer to a (read-only copy of a) line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002549 *
2550 * On failure an error message is given and IObuff is returned (to avoid
2551 * having to check for error everywhere).
2552 */
2553 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002554ml_get(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002555{
2556 return ml_get_buf(curbuf, lnum, FALSE);
2557}
2558
2559/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002560 * Return pointer to position "pos".
Bram Moolenaar071d4272004-06-13 20:20:40 +00002561 */
2562 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002563ml_get_pos(pos_T *pos)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002564{
2565 return (ml_get_buf(curbuf, pos->lnum, FALSE) + pos->col);
2566}
2567
2568/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002569 * Return pointer to cursor line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002570 */
2571 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002572ml_get_curline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002573{
2574 return ml_get_buf(curbuf, curwin->w_cursor.lnum, FALSE);
2575}
2576
2577/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002578 * Return pointer to cursor position.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002579 */
2580 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002581ml_get_cursor(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002582{
2583 return (ml_get_buf(curbuf, curwin->w_cursor.lnum, FALSE) +
2584 curwin->w_cursor.col);
2585}
2586
2587/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002588 * Return a pointer to a line in a specific buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +00002589 *
2590 * "will_change": if TRUE mark the buffer dirty (chars in the line will be
2591 * changed)
2592 */
2593 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002594ml_get_buf(
2595 buf_T *buf,
2596 linenr_T lnum,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002597 int will_change) // line will be changed
Bram Moolenaar071d4272004-06-13 20:20:40 +00002598{
Bram Moolenaarad40f022007-02-13 03:01:39 +00002599 bhdr_T *hp;
2600 DATA_BL *dp;
Bram Moolenaarad40f022007-02-13 03:01:39 +00002601 static int recursive = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002602
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002603 if (lnum > buf->b_ml.ml_line_count) // invalid line number
Bram Moolenaar071d4272004-06-13 20:20:40 +00002604 {
Bram Moolenaarad40f022007-02-13 03:01:39 +00002605 if (recursive == 0)
2606 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002607 // Avoid giving this message for a recursive call, may happen when
2608 // the GUI redraws part of the text.
Bram Moolenaarad40f022007-02-13 03:01:39 +00002609 ++recursive;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002610 siemsg(_("E315: ml_get: invalid lnum: %ld"), lnum);
Bram Moolenaarad40f022007-02-13 03:01:39 +00002611 --recursive;
2612 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002613errorret:
2614 STRCPY(IObuff, "???");
Bram Moolenaaradfde112019-05-25 22:11:45 +02002615 buf->b_ml.ml_line_len = 4;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002616 return IObuff;
2617 }
Bram Moolenaaradfde112019-05-25 22:11:45 +02002618 if (lnum <= 0) // pretend line 0 is line 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00002619 lnum = 1;
2620
Bram Moolenaaradfde112019-05-25 22:11:45 +02002621 if (buf->b_ml.ml_mfp == NULL) // there are no lines
2622 {
2623 buf->b_ml.ml_line_len = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002624 return (char_u *)"";
Bram Moolenaaradfde112019-05-25 22:11:45 +02002625 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002626
Bram Moolenaar37d619f2010-03-10 14:46:26 +01002627 /*
2628 * See if it is the same line as requested last time.
2629 * Otherwise may need to flush last used line.
2630 * Don't use the last used line when 'swapfile' is reset, need to load all
2631 * blocks.
2632 */
Bram Moolenaar47b8b152007-02-07 02:41:57 +00002633 if (buf->b_ml.ml_line_lnum != lnum || mf_dont_release)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002634 {
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002635 unsigned start, end;
2636 colnr_T len;
2637 int idx;
2638
Bram Moolenaar071d4272004-06-13 20:20:40 +00002639 ml_flush_line(buf);
2640
2641 /*
2642 * Find the data block containing the line.
2643 * This also fills the stack with the blocks from the root to the data
2644 * block and releases any locked block.
2645 */
2646 if ((hp = ml_find_line(buf, lnum, ML_FIND)) == NULL)
2647 {
Bram Moolenaarad40f022007-02-13 03:01:39 +00002648 if (recursive == 0)
2649 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002650 // Avoid giving this message for a recursive call, may happen
2651 // when the GUI redraws part of the text.
Bram Moolenaarad40f022007-02-13 03:01:39 +00002652 ++recursive;
Bram Moolenaarcb868932019-10-26 20:56:21 +02002653 get_trans_bufname(buf);
2654 shorten_dir(NameBuff);
2655 siemsg(_("E316: ml_get: cannot find line %ld in buffer %d %s"),
2656 lnum, buf->b_fnum, NameBuff);
Bram Moolenaarad40f022007-02-13 03:01:39 +00002657 --recursive;
2658 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002659 goto errorret;
2660 }
2661
2662 dp = (DATA_BL *)(hp->bh_data);
2663
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002664 idx = lnum - buf->b_ml.ml_locked_low;
2665 start = ((dp->db_index[idx]) & DB_INDEX_MASK);
2666 // The text ends where the previous line starts. The first line ends
2667 // at the end of the block.
2668 if (idx == 0)
2669 end = dp->db_txt_end;
2670 else
2671 end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK);
2672 len = end - start;
2673
2674 buf->b_ml.ml_line_ptr = (char_u *)dp + start;
2675 buf->b_ml.ml_line_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002676 buf->b_ml.ml_line_lnum = lnum;
2677 buf->b_ml.ml_flags &= ~ML_LINE_DIRTY;
2678 }
2679 if (will_change)
2680 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
2681
2682 return buf->b_ml.ml_line_ptr;
2683}
2684
2685/*
2686 * Check if a line that was just obtained by a call to ml_get
2687 * is in allocated memory.
2688 */
2689 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002690ml_line_alloced(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002691{
2692 return (curbuf->b_ml.ml_flags & ML_LINE_DIRTY);
2693}
2694
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002695#ifdef FEAT_PROP_POPUP
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002696/*
2697 * Add text properties that continue from the previous line.
2698 */
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002699 static void
2700add_text_props_for_append(
2701 buf_T *buf,
2702 linenr_T lnum,
2703 char_u **line,
2704 int *len,
2705 char_u **tofree)
2706{
2707 int round;
2708 int new_prop_count = 0;
2709 int count;
2710 int n;
2711 char_u *props;
Bram Moolenaarea781452019-09-04 18:53:12 +02002712 int new_len = 0; // init for gcc
Bram Moolenaar8f13d822020-09-12 21:04:23 +02002713 char_u *new_line = NULL;
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002714 textprop_T prop;
2715
2716 // Make two rounds:
2717 // 1. calculate the extra space needed
2718 // 2. allocate the space and fill it
2719 for (round = 1; round <= 2; ++round)
2720 {
2721 if (round == 2)
2722 {
2723 if (new_prop_count == 0)
2724 return; // nothing to do
2725 new_len = *len + new_prop_count * sizeof(textprop_T);
Bram Moolenaar964b3742019-05-24 18:54:09 +02002726 new_line = alloc(new_len);
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002727 if (new_line == NULL)
2728 return;
2729 mch_memmove(new_line, *line, *len);
2730 new_prop_count = 0;
2731 }
2732
2733 // Get the line above to find any props that continue in the next
2734 // line.
2735 count = get_text_props(buf, lnum, &props, FALSE);
2736 for (n = 0; n < count; ++n)
2737 {
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002738 mch_memmove(&prop, props + n * sizeof(textprop_T),
2739 sizeof(textprop_T));
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002740 if (prop.tp_flags & TP_FLAG_CONT_NEXT)
2741 {
2742 if (round == 2)
2743 {
2744 prop.tp_flags |= TP_FLAG_CONT_PREV;
2745 prop.tp_col = 1;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002746 prop.tp_len = *len; // not exactly the right length
2747 mch_memmove(new_line + *len + new_prop_count
2748 * sizeof(textprop_T), &prop, sizeof(textprop_T));
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002749 }
2750 ++new_prop_count;
2751 }
2752 }
2753 }
2754 *line = new_line;
2755 *tofree = new_line;
2756 *len = new_len;
2757}
2758#endif
2759
Bram Moolenaar071d4272004-06-13 20:20:40 +00002760 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002761ml_append_int(
2762 buf_T *buf,
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002763 linenr_T lnum, // append after this line (can be 0)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002764 char_u *line_arg, // text of the new line
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002765 colnr_T len_arg, // length of line, including NUL, or 0
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002766 int flags) // ML_APPEND_ flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00002767{
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002768 char_u *line = line_arg;
2769 colnr_T len = len_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002770 int i;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002771 int line_count; // number of indexes in current block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002772 int offset;
2773 int from, to;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002774 int space_needed; // space needed for new line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002775 int page_size;
2776 int page_count;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002777 int db_idx; // index for lnum in data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002778 bhdr_T *hp;
2779 memfile_T *mfp;
2780 DATA_BL *dp;
2781 PTR_BL *pp;
2782 infoptr_T *ip;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002783#ifdef FEAT_PROP_POPUP
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002784 char_u *tofree = NULL;
2785#endif
2786 int ret = FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002787
Bram Moolenaar071d4272004-06-13 20:20:40 +00002788 if (lnum > buf->b_ml.ml_line_count || buf->b_ml.ml_mfp == NULL)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002789 return FAIL; // lnum out of range
Bram Moolenaar071d4272004-06-13 20:20:40 +00002790
2791 if (lowest_marked && lowest_marked > lnum)
2792 lowest_marked = lnum + 1;
2793
2794 if (len == 0)
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002795 len = (colnr_T)STRLEN(line) + 1; // space needed for the text
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002796
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002797#ifdef FEAT_PROP_POPUP
Bram Moolenaar840f91f2021-05-26 22:32:10 +02002798 if (curbuf->b_has_textprop && lnum > 0
2799 && !(flags & (ML_APPEND_UNDO | ML_APPEND_NOPROP)))
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002800 // Add text properties that continue from the previous line.
2801 add_text_props_for_append(buf, lnum, &line, &len, &tofree);
2802#endif
2803
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002804 space_needed = len + INDEX_SIZE; // space needed for text + index
Bram Moolenaar071d4272004-06-13 20:20:40 +00002805
2806 mfp = buf->b_ml.ml_mfp;
2807 page_size = mfp->mf_page_size;
2808
2809/*
2810 * find the data block containing the previous line
2811 * This also fills the stack with the blocks from the root to the data block
2812 * This also releases any locked block.
2813 */
2814 if ((hp = ml_find_line(buf, lnum == 0 ? (linenr_T)1 : lnum,
2815 ML_INSERT)) == NULL)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002816 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002817
2818 buf->b_ml.ml_flags &= ~ML_EMPTY;
2819
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002820 if (lnum == 0) // got line one instead, correct db_idx
2821 db_idx = -1; // careful, it is negative!
Bram Moolenaar071d4272004-06-13 20:20:40 +00002822 else
2823 db_idx = lnum - buf->b_ml.ml_locked_low;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002824 // get line count before the insertion
Bram Moolenaar071d4272004-06-13 20:20:40 +00002825 line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low;
2826
2827 dp = (DATA_BL *)(hp->bh_data);
2828
2829/*
2830 * If
2831 * - there is not enough room in the current block
2832 * - appending to the last line in the block
2833 * - not appending to the last line in the file
2834 * insert in front of the next block.
2835 */
2836 if ((int)dp->db_free < space_needed && db_idx == line_count - 1
2837 && lnum < buf->b_ml.ml_line_count)
2838 {
2839 /*
2840 * Now that the line is not going to be inserted in the block that we
2841 * expected, the line count has to be adjusted in the pointer blocks
2842 * by using ml_locked_lineadd.
2843 */
2844 --(buf->b_ml.ml_locked_lineadd);
2845 --(buf->b_ml.ml_locked_high);
2846 if ((hp = ml_find_line(buf, lnum + 1, ML_INSERT)) == NULL)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002847 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002848
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002849 db_idx = -1; // careful, it is negative!
2850 // get line count before the insertion
Bram Moolenaar071d4272004-06-13 20:20:40 +00002851 line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low;
2852 CHECK(buf->b_ml.ml_locked_low != lnum + 1, "locked_low != lnum + 1");
2853
2854 dp = (DATA_BL *)(hp->bh_data);
2855 }
2856
2857 ++buf->b_ml.ml_line_count;
2858
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002859 if ((int)dp->db_free >= space_needed) // enough room in data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002860 {
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002861 /*
2862 * Insert the new line in an existing data block, or in the data block
2863 * allocated above.
2864 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002865 dp->db_txt_start -= len;
2866 dp->db_free -= space_needed;
2867 ++(dp->db_line_count);
2868
2869 /*
2870 * move the text of the lines that follow to the front
2871 * adjust the indexes of the lines that follow
2872 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002873 if (line_count > db_idx + 1) // if there are following lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00002874 {
2875 /*
2876 * Offset is the start of the previous line.
2877 * This will become the character just after the new line.
2878 */
2879 if (db_idx < 0)
2880 offset = dp->db_txt_end;
2881 else
2882 offset = ((dp->db_index[db_idx]) & DB_INDEX_MASK);
2883 mch_memmove((char *)dp + dp->db_txt_start,
2884 (char *)dp + dp->db_txt_start + len,
2885 (size_t)(offset - (dp->db_txt_start + len)));
2886 for (i = line_count - 1; i > db_idx; --i)
2887 dp->db_index[i + 1] = dp->db_index[i] - len;
2888 dp->db_index[db_idx + 1] = offset - len;
2889 }
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002890 else
2891 // add line at the end (which is the start of the text)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002892 dp->db_index[db_idx + 1] = dp->db_txt_start;
2893
2894 /*
2895 * copy the text into the block
2896 */
2897 mch_memmove((char *)dp + dp->db_index[db_idx + 1], line, (size_t)len);
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002898 if (flags & ML_APPEND_MARK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002899 dp->db_index[db_idx + 1] |= DB_MARKED;
2900
2901 /*
2902 * Mark the block dirty.
2903 */
2904 buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002905 if (!(flags & ML_APPEND_NEW))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002906 buf->b_ml.ml_flags |= ML_LOCKED_POS;
2907 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002908 else // not enough space in data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002909 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002910 long line_count_left, line_count_right;
2911 int page_count_left, page_count_right;
2912 bhdr_T *hp_left;
2913 bhdr_T *hp_right;
2914 bhdr_T *hp_new;
2915 int lines_moved;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002916 int data_moved = 0; // init to shut up gcc
2917 int total_moved = 0; // init to shut up gcc
Bram Moolenaar071d4272004-06-13 20:20:40 +00002918 DATA_BL *dp_right, *dp_left;
2919 int stack_idx;
2920 int in_left;
2921 int lineadd;
2922 blocknr_T bnum_left, bnum_right;
2923 linenr_T lnum_left, lnum_right;
2924 int pb_idx;
2925 PTR_BL *pp_new;
2926
2927 /*
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002928 * There is not enough room, we have to create a new data block and
2929 * copy some lines into it.
2930 * Then we have to insert an entry in the pointer block.
2931 * If this pointer block also is full, we go up another block, and so
2932 * on, up to the root if necessary.
2933 * The line counts in the pointer blocks have already been adjusted by
2934 * ml_find_line().
2935 *
Bram Moolenaar071d4272004-06-13 20:20:40 +00002936 * We are going to allocate a new data block. Depending on the
2937 * situation it will be put to the left or right of the existing
2938 * block. If possible we put the new line in the left block and move
2939 * the lines after it to the right block. Otherwise the new line is
2940 * also put in the right block. This method is more efficient when
2941 * inserting a lot of lines at one place.
2942 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002943 if (db_idx < 0) // left block is new, right block is existing
Bram Moolenaar071d4272004-06-13 20:20:40 +00002944 {
2945 lines_moved = 0;
2946 in_left = TRUE;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002947 // space_needed does not change
Bram Moolenaar071d4272004-06-13 20:20:40 +00002948 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002949 else // left block is existing, right block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00002950 {
2951 lines_moved = line_count - db_idx - 1;
2952 if (lines_moved == 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002953 in_left = FALSE; // put new line in right block
2954 // space_needed does not change
Bram Moolenaar071d4272004-06-13 20:20:40 +00002955 else
2956 {
2957 data_moved = ((dp->db_index[db_idx]) & DB_INDEX_MASK) -
2958 dp->db_txt_start;
2959 total_moved = data_moved + lines_moved * INDEX_SIZE;
2960 if ((int)dp->db_free + total_moved >= space_needed)
2961 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002962 in_left = TRUE; // put new line in left block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002963 space_needed = total_moved;
2964 }
2965 else
2966 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002967 in_left = FALSE; // put new line in right block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002968 space_needed += total_moved;
2969 }
2970 }
2971 }
2972
2973 page_count = ((space_needed + HEADER_SIZE) + page_size - 1) / page_size;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002974 if ((hp_new = ml_new_data(mfp, flags & ML_APPEND_NEW, page_count))
2975 == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002976 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002977 // correct line counts in pointer blocks
Bram Moolenaar071d4272004-06-13 20:20:40 +00002978 --(buf->b_ml.ml_locked_lineadd);
2979 --(buf->b_ml.ml_locked_high);
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002980 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002981 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002982 if (db_idx < 0) // left block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00002983 {
2984 hp_left = hp_new;
2985 hp_right = hp;
2986 line_count_left = 0;
2987 line_count_right = line_count;
2988 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002989 else // right block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00002990 {
2991 hp_left = hp;
2992 hp_right = hp_new;
2993 line_count_left = line_count;
2994 line_count_right = 0;
2995 }
2996 dp_right = (DATA_BL *)(hp_right->bh_data);
2997 dp_left = (DATA_BL *)(hp_left->bh_data);
2998 bnum_left = hp_left->bh_bnum;
2999 bnum_right = hp_right->bh_bnum;
3000 page_count_left = hp_left->bh_page_count;
3001 page_count_right = hp_right->bh_page_count;
3002
3003 /*
3004 * May move the new line into the right/new block.
3005 */
3006 if (!in_left)
3007 {
3008 dp_right->db_txt_start -= len;
3009 dp_right->db_free -= len + INDEX_SIZE;
3010 dp_right->db_index[0] = dp_right->db_txt_start;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003011 if (flags & ML_APPEND_MARK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003012 dp_right->db_index[0] |= DB_MARKED;
3013
3014 mch_memmove((char *)dp_right + dp_right->db_txt_start,
3015 line, (size_t)len);
3016 ++line_count_right;
3017 }
3018 /*
3019 * may move lines from the left/old block to the right/new one.
3020 */
3021 if (lines_moved)
3022 {
3023 /*
3024 */
3025 dp_right->db_txt_start -= data_moved;
3026 dp_right->db_free -= total_moved;
3027 mch_memmove((char *)dp_right + dp_right->db_txt_start,
3028 (char *)dp_left + dp_left->db_txt_start,
3029 (size_t)data_moved);
3030 offset = dp_right->db_txt_start - dp_left->db_txt_start;
3031 dp_left->db_txt_start += data_moved;
3032 dp_left->db_free += total_moved;
3033
3034 /*
3035 * update indexes in the new block
3036 */
3037 for (to = line_count_right, from = db_idx + 1;
3038 from < line_count_left; ++from, ++to)
3039 dp_right->db_index[to] = dp->db_index[from] + offset;
3040 line_count_right += lines_moved;
3041 line_count_left -= lines_moved;
3042 }
3043
3044 /*
3045 * May move the new line into the left (old or new) block.
3046 */
3047 if (in_left)
3048 {
3049 dp_left->db_txt_start -= len;
3050 dp_left->db_free -= len + INDEX_SIZE;
3051 dp_left->db_index[line_count_left] = dp_left->db_txt_start;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003052 if (flags & ML_APPEND_MARK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003053 dp_left->db_index[line_count_left] |= DB_MARKED;
3054 mch_memmove((char *)dp_left + dp_left->db_txt_start,
3055 line, (size_t)len);
3056 ++line_count_left;
3057 }
3058
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003059 if (db_idx < 0) // left block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00003060 {
3061 lnum_left = lnum + 1;
3062 lnum_right = 0;
3063 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003064 else // right block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00003065 {
3066 lnum_left = 0;
3067 if (in_left)
3068 lnum_right = lnum + 2;
3069 else
3070 lnum_right = lnum + 1;
3071 }
3072 dp_left->db_line_count = line_count_left;
3073 dp_right->db_line_count = line_count_right;
3074
3075 /*
3076 * release the two data blocks
3077 * The new one (hp_new) already has a correct blocknumber.
3078 * The old one (hp, in ml_locked) gets a positive blocknumber if
3079 * we changed it and we are not editing a new file.
3080 */
3081 if (lines_moved || in_left)
3082 buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003083 if (!(flags & ML_APPEND_NEW) && db_idx >= 0 && in_left)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003084 buf->b_ml.ml_flags |= ML_LOCKED_POS;
3085 mf_put(mfp, hp_new, TRUE, FALSE);
3086
3087 /*
3088 * flush the old data block
3089 * set ml_locked_lineadd to 0, because the updating of the
3090 * pointer blocks is done below
3091 */
3092 lineadd = buf->b_ml.ml_locked_lineadd;
3093 buf->b_ml.ml_locked_lineadd = 0;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003094 ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003095
3096 /*
3097 * update pointer blocks for the new data block
3098 */
3099 for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0;
3100 --stack_idx)
3101 {
3102 ip = &(buf->b_ml.ml_stack[stack_idx]);
3103 pb_idx = ip->ip_index;
3104 if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003105 goto theend;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003106 pp = (PTR_BL *)(hp->bh_data); // must be pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003107 if (pp->pb_id != PTR_ID)
3108 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003109 iemsg(_("E317: pointer block id wrong 3"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003110 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003111 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003112 }
3113 /*
3114 * TODO: If the pointer block is full and we are adding at the end
3115 * try to insert in front of the next block
3116 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003117 // block not full, add one entry
Bram Moolenaar071d4272004-06-13 20:20:40 +00003118 if (pp->pb_count < pp->pb_count_max)
3119 {
3120 if (pb_idx + 1 < (int)pp->pb_count)
3121 mch_memmove(&pp->pb_pointer[pb_idx + 2],
3122 &pp->pb_pointer[pb_idx + 1],
3123 (size_t)(pp->pb_count - pb_idx - 1) * sizeof(PTR_EN));
3124 ++pp->pb_count;
3125 pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
3126 pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
3127 pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
3128 pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right;
3129 pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right;
3130 pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right;
3131
3132 if (lnum_left != 0)
3133 pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left;
3134 if (lnum_right != 0)
3135 pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right;
3136
3137 mf_put(mfp, hp, TRUE, FALSE);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003138 buf->b_ml.ml_stack_top = stack_idx + 1; // truncate stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00003139
3140 if (lineadd)
3141 {
3142 --(buf->b_ml.ml_stack_top);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003143 // fix line count for rest of blocks in the stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00003144 ml_lineadd(buf, lineadd);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003145 // fix stack itself
Bram Moolenaar071d4272004-06-13 20:20:40 +00003146 buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high +=
3147 lineadd;
3148 ++(buf->b_ml.ml_stack_top);
3149 }
3150
3151 /*
3152 * We are finished, break the loop here.
3153 */
3154 break;
3155 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003156 else // pointer block full
Bram Moolenaar071d4272004-06-13 20:20:40 +00003157 {
3158 /*
3159 * split the pointer block
3160 * allocate a new pointer block
3161 * move some of the pointer into the new block
3162 * prepare for updating the parent block
3163 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003164 for (;;) // do this twice when splitting block 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00003165 {
3166 hp_new = ml_new_ptr(mfp);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003167 if (hp_new == NULL) // TODO: try to fix tree
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003168 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003169 pp_new = (PTR_BL *)(hp_new->bh_data);
3170
3171 if (hp->bh_bnum != 1)
3172 break;
3173
3174 /*
3175 * if block 1 becomes full the tree is given an extra level
3176 * The pointers from block 1 are moved into the new block.
3177 * block 1 is updated to point to the new block
3178 * then continue to split the new block
3179 */
3180 mch_memmove(pp_new, pp, (size_t)page_size);
3181 pp->pb_count = 1;
3182 pp->pb_pointer[0].pe_bnum = hp_new->bh_bnum;
3183 pp->pb_pointer[0].pe_line_count = buf->b_ml.ml_line_count;
3184 pp->pb_pointer[0].pe_old_lnum = 1;
3185 pp->pb_pointer[0].pe_page_count = 1;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003186 mf_put(mfp, hp, TRUE, FALSE); // release block 1
3187 hp = hp_new; // new block is to be split
Bram Moolenaar071d4272004-06-13 20:20:40 +00003188 pp = pp_new;
3189 CHECK(stack_idx != 0, _("stack_idx should be 0"));
3190 ip->ip_index = 0;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003191 ++stack_idx; // do block 1 again later
Bram Moolenaar071d4272004-06-13 20:20:40 +00003192 }
3193 /*
3194 * move the pointers after the current one to the new block
3195 * If there are none, the new entry will be in the new block.
3196 */
3197 total_moved = pp->pb_count - pb_idx - 1;
3198 if (total_moved)
3199 {
3200 mch_memmove(&pp_new->pb_pointer[0],
3201 &pp->pb_pointer[pb_idx + 1],
3202 (size_t)(total_moved) * sizeof(PTR_EN));
3203 pp_new->pb_count = total_moved;
3204 pp->pb_count -= total_moved - 1;
3205 pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right;
3206 pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right;
3207 pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right;
3208 if (lnum_right)
3209 pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right;
3210 }
3211 else
3212 {
3213 pp_new->pb_count = 1;
3214 pp_new->pb_pointer[0].pe_bnum = bnum_right;
3215 pp_new->pb_pointer[0].pe_line_count = line_count_right;
3216 pp_new->pb_pointer[0].pe_page_count = page_count_right;
3217 pp_new->pb_pointer[0].pe_old_lnum = lnum_right;
3218 }
3219 pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
3220 pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
3221 pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
3222 if (lnum_left)
3223 pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left;
3224 lnum_left = 0;
3225 lnum_right = 0;
3226
3227 /*
3228 * recompute line counts
3229 */
3230 line_count_right = 0;
3231 for (i = 0; i < (int)pp_new->pb_count; ++i)
3232 line_count_right += pp_new->pb_pointer[i].pe_line_count;
3233 line_count_left = 0;
3234 for (i = 0; i < (int)pp->pb_count; ++i)
3235 line_count_left += pp->pb_pointer[i].pe_line_count;
3236
3237 bnum_left = hp->bh_bnum;
3238 bnum_right = hp_new->bh_bnum;
3239 page_count_left = 1;
3240 page_count_right = 1;
3241 mf_put(mfp, hp, TRUE, FALSE);
3242 mf_put(mfp, hp_new, TRUE, FALSE);
3243 }
3244 }
3245
3246 /*
3247 * Safety check: fallen out of for loop?
3248 */
3249 if (stack_idx < 0)
3250 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003251 iemsg(_("E318: Updated too many blocks?"));
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003252 buf->b_ml.ml_stack_top = 0; // invalidate stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00003253 }
3254 }
3255
3256#ifdef FEAT_BYTEOFF
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003257 // The line was inserted below 'lnum'
Bram Moolenaar071d4272004-06-13 20:20:40 +00003258 ml_updatechunk(buf, lnum + 1, (long)len, ML_CHNK_ADDLINE);
3259#endif
3260#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02003261 if (netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +00003262 {
3263 if (STRLEN(line) > 0)
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003264 netbeans_inserted(buf, lnum+1, (colnr_T)0, line, (int)STRLEN(line));
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00003265 netbeans_inserted(buf, lnum+1, (colnr_T)STRLEN(line),
Bram Moolenaar071d4272004-06-13 20:20:40 +00003266 (char_u *)"\n", 1);
3267 }
3268#endif
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01003269#ifdef FEAT_JOB_CHANNEL
Bram Moolenaar99ef0622016-03-06 20:22:25 +01003270 if (buf->b_write_to_channel)
3271 channel_write_new_lines(buf);
3272#endif
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003273 ret = OK;
Bram Moolenaar99ef0622016-03-06 20:22:25 +01003274
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003275theend:
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003276#ifdef FEAT_PROP_POPUP
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003277 vim_free(tofree);
3278#endif
3279 return ret;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003280}
3281
3282/*
Bram Moolenaar250e3112019-07-15 23:02:14 +02003283 * Flush any pending change and call ml_append_int()
3284 */
3285 static int
3286ml_append_flush(
3287 buf_T *buf,
3288 linenr_T lnum, // append after this line (can be 0)
3289 char_u *line, // text of the new line
3290 colnr_T len, // length of line, including NUL, or 0
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003291 int flags) // ML_APPEND_ flags
Bram Moolenaar250e3112019-07-15 23:02:14 +02003292{
3293 if (lnum > buf->b_ml.ml_line_count)
3294 return FAIL; // lnum out of range
3295
3296 if (buf->b_ml.ml_line_lnum != 0)
3297 // This may also invoke ml_append_int().
3298 ml_flush_line(buf);
3299
3300#ifdef FEAT_EVAL
3301 // When inserting above recorded changes: flush the changes before changing
3302 // the text. Then flush the cached line, it may become invalid.
3303 may_invoke_listeners(buf, lnum + 1, lnum + 1, 1);
3304 if (buf->b_ml.ml_line_lnum != 0)
3305 ml_flush_line(buf);
3306#endif
3307
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003308 return ml_append_int(buf, lnum, line, len, flags);
Bram Moolenaar250e3112019-07-15 23:02:14 +02003309}
3310
3311/*
3312 * Append a line after lnum (may be 0 to insert a line in front of the file).
3313 * "line" does not need to be allocated, but can't be another line in a
3314 * buffer, unlocking may make it invalid.
3315 *
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003316 * "newfile": TRUE when starting to edit a new file, meaning that pe_old_lnum
Bram Moolenaar250e3112019-07-15 23:02:14 +02003317 * will be set for recovery
3318 * Check: The caller of this function should probably also call
3319 * appended_lines().
3320 *
3321 * return FAIL for failure, OK otherwise
3322 */
3323 int
3324ml_append(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003325 linenr_T lnum, // append after this line (can be 0)
3326 char_u *line, // text of the new line
3327 colnr_T len, // length of new line, including NUL, or 0
3328 int newfile) // flag, see above
Bram Moolenaar250e3112019-07-15 23:02:14 +02003329{
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003330 return ml_append_flags(lnum, line, len, newfile ? ML_APPEND_NEW : 0);
3331}
3332
3333 int
3334ml_append_flags(
3335 linenr_T lnum, // append after this line (can be 0)
3336 char_u *line, // text of the new line
3337 colnr_T len, // length of new line, including NUL, or 0
3338 int flags) // ML_APPEND_ values
3339{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003340 // When starting up, we might still need to create the memfile
Bram Moolenaar250e3112019-07-15 23:02:14 +02003341 if (curbuf->b_ml.ml_mfp == NULL && open_buffer(FALSE, NULL, 0) == FAIL)
3342 return FAIL;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003343 return ml_append_flush(curbuf, lnum, line, len, flags);
Bram Moolenaar250e3112019-07-15 23:02:14 +02003344}
3345
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003346
Bram Moolenaar250e3112019-07-15 23:02:14 +02003347#if defined(FEAT_SPELL) || defined(FEAT_QUICKFIX) || defined(PROTO)
3348/*
3349 * Like ml_append() but for an arbitrary buffer. The buffer must already have
3350 * a memline.
3351 */
3352 int
3353ml_append_buf(
3354 buf_T *buf,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003355 linenr_T lnum, // append after this line (can be 0)
3356 char_u *line, // text of the new line
3357 colnr_T len, // length of new line, including NUL, or 0
3358 int newfile) // flag, see above
Bram Moolenaar250e3112019-07-15 23:02:14 +02003359{
3360 if (buf->b_ml.ml_mfp == NULL)
3361 return FAIL;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003362 return ml_append_flush(buf, lnum, line, len, newfile ? ML_APPEND_NEW : 0);
Bram Moolenaar250e3112019-07-15 23:02:14 +02003363}
3364#endif
3365
3366/*
Bram Moolenaar5966ea12020-07-15 15:30:05 +02003367 * Replace line "lnum", with buffering, in current buffer.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003368 *
Bram Moolenaar1056d982006-03-09 22:37:52 +00003369 * If "copy" is TRUE, make a copy of the line, otherwise the line has been
Bram Moolenaar071d4272004-06-13 20:20:40 +00003370 * copied to allocated memory already.
Bram Moolenaar5966ea12020-07-15 15:30:05 +02003371 * If "copy" is FALSE the "line" may be freed to add text properties!
3372 * Do not use it after calling ml_replace().
Bram Moolenaar071d4272004-06-13 20:20:40 +00003373 *
3374 * Check: The caller of this function should probably also call
3375 * changed_lines(), unless update_screen(NOT_VALID) is used.
3376 *
3377 * return FAIL for failure, OK otherwise
3378 */
3379 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003380ml_replace(linenr_T lnum, char_u *line, int copy)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003381{
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003382 colnr_T len = -1;
3383
3384 if (line != NULL)
Bram Moolenaar4efe73b2018-12-16 14:37:39 +01003385 len = (colnr_T)STRLEN(line);
Bram Moolenaarccae4672019-01-04 15:09:57 +01003386 return ml_replace_len(lnum, line, len, FALSE, copy);
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003387}
3388
Bram Moolenaarccae4672019-01-04 15:09:57 +01003389/*
3390 * Replace a line for the current buffer. Like ml_replace() with:
3391 * "len_arg" is the length of the text, excluding NUL.
3392 * If "has_props" is TRUE then "line_arg" includes the text properties and
3393 * "len_arg" includes the NUL of the text.
3394 */
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003395 int
Bram Moolenaarccae4672019-01-04 15:09:57 +01003396ml_replace_len(
3397 linenr_T lnum,
3398 char_u *line_arg,
3399 colnr_T len_arg,
3400 int has_props,
3401 int copy)
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003402{
3403 char_u *line = line_arg;
3404 colnr_T len = len_arg;
3405
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003406 if (line == NULL) // just checking...
Bram Moolenaar071d4272004-06-13 20:20:40 +00003407 return FAIL;
3408
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003409 // When starting up, we might still need to create the memfile
Bram Moolenaar59f931e2010-07-24 20:27:03 +02003410 if (curbuf->b_ml.ml_mfp == NULL && open_buffer(FALSE, NULL, 0) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003411 return FAIL;
3412
Bram Moolenaarccae4672019-01-04 15:09:57 +01003413 if (!has_props)
3414 ++len; // include the NUL after the text
3415 if (copy)
3416 {
3417 // copy the line to allocated memory
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003418#ifdef FEAT_PROP_POPUP
Bram Moolenaarccae4672019-01-04 15:09:57 +01003419 if (has_props)
3420 line = vim_memsave(line, len);
3421 else
3422#endif
3423 line = vim_strnsave(line, len - 1);
3424 if (line == NULL)
3425 return FAIL;
3426 }
3427
Bram Moolenaar071d4272004-06-13 20:20:40 +00003428#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02003429 if (netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +00003430 {
3431 netbeans_removed(curbuf, lnum, 0, (long)STRLEN(ml_get(lnum)));
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003432 netbeans_inserted(curbuf, lnum, 0, line, (int)STRLEN(line));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003433 }
3434#endif
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003435 if (curbuf->b_ml.ml_line_lnum != lnum)
3436 {
3437 // another line is buffered, flush it
3438 ml_flush_line(curbuf);
Bram Moolenaarca79a5f2018-12-13 23:16:36 +01003439 curbuf->b_ml.ml_flags &= ~ML_LINE_DIRTY;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003440
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003441#ifdef FEAT_PROP_POPUP
Bram Moolenaarccae4672019-01-04 15:09:57 +01003442 if (curbuf->b_has_textprop && !has_props)
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003443 // Need to fetch the old line to copy over any text properties.
3444 ml_get_buf(curbuf, lnum, TRUE);
3445#endif
3446 }
3447
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003448#ifdef FEAT_PROP_POPUP
Bram Moolenaarccae4672019-01-04 15:09:57 +01003449 if (curbuf->b_has_textprop && !has_props)
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003450 {
3451 size_t oldtextlen = STRLEN(curbuf->b_ml.ml_line_ptr) + 1;
3452
3453 if (oldtextlen < (size_t)curbuf->b_ml.ml_line_len)
3454 {
3455 char_u *newline;
3456 size_t textproplen = curbuf->b_ml.ml_line_len - oldtextlen;
3457
3458 // Need to copy over text properties, stored after the text.
Bram Moolenaarccae4672019-01-04 15:09:57 +01003459 newline = alloc(len + (int)textproplen);
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003460 if (newline != NULL)
3461 {
Bram Moolenaarccae4672019-01-04 15:09:57 +01003462 mch_memmove(newline, line, len);
Bram Moolenaar0d3cb732019-05-18 13:05:18 +02003463 mch_memmove(newline + len, curbuf->b_ml.ml_line_ptr
3464 + oldtextlen, textproplen);
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003465 vim_free(line);
3466 line = newline;
Bram Moolenaar4efe73b2018-12-16 14:37:39 +01003467 len += (colnr_T)textproplen;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003468 }
3469 }
3470 }
3471#endif
3472
Bram Moolenaarccae4672019-01-04 15:09:57 +01003473 if (curbuf->b_ml.ml_flags & ML_LINE_DIRTY) // same line allocated
3474 vim_free(curbuf->b_ml.ml_line_ptr); // free it
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003475
Bram Moolenaar071d4272004-06-13 20:20:40 +00003476 curbuf->b_ml.ml_line_ptr = line;
Bram Moolenaarccae4672019-01-04 15:09:57 +01003477 curbuf->b_ml.ml_line_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003478 curbuf->b_ml.ml_line_lnum = lnum;
3479 curbuf->b_ml.ml_flags = (curbuf->b_ml.ml_flags | ML_LINE_DIRTY) & ~ML_EMPTY;
3480
3481 return OK;
3482}
3483
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003484#ifdef FEAT_PROP_POPUP
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003485/*
3486 * Adjust text properties in line "lnum" for a deleted line.
3487 * When "above" is true this is the line above the deleted line.
3488 * "del_props" are the properties of the deleted line.
3489 */
3490 static void
3491adjust_text_props_for_delete(
3492 buf_T *buf,
3493 linenr_T lnum,
3494 char_u *del_props,
3495 int del_props_len,
3496 int above)
3497{
3498 int did_get_line = FALSE;
3499 int done_del;
3500 int done_this;
3501 textprop_T prop_del;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003502 bhdr_T *hp;
3503 DATA_BL *dp;
3504 int idx;
3505 int line_start;
3506 long line_size;
3507 int this_props_len;
3508 char_u *text;
3509 size_t textlen;
3510 int found;
3511
3512 for (done_del = 0; done_del < del_props_len; done_del += sizeof(textprop_T))
3513 {
3514 mch_memmove(&prop_del, del_props + done_del, sizeof(textprop_T));
3515 if ((above && (prop_del.tp_flags & TP_FLAG_CONT_PREV)
3516 && !(prop_del.tp_flags & TP_FLAG_CONT_NEXT))
3517 || (!above && (prop_del.tp_flags & TP_FLAG_CONT_NEXT)
3518 && !(prop_del.tp_flags & TP_FLAG_CONT_PREV)))
3519 {
3520 if (!did_get_line)
3521 {
3522 did_get_line = TRUE;
3523 if ((hp = ml_find_line(buf, lnum, ML_FIND)) == NULL)
3524 return;
3525
3526 dp = (DATA_BL *)(hp->bh_data);
3527 idx = lnum - buf->b_ml.ml_locked_low;
3528 line_start = ((dp->db_index[idx]) & DB_INDEX_MASK);
3529 if (idx == 0) // first line in block, text at the end
3530 line_size = dp->db_txt_end - line_start;
3531 else
Bram Moolenaar87be9be2020-05-30 15:32:02 +02003532 line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK)
3533 - line_start;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003534 text = (char_u *)dp + line_start;
3535 textlen = STRLEN(text) + 1;
3536 if ((long)textlen >= line_size)
3537 {
3538 if (above)
3539 internal_error("no text property above deleted line");
3540 else
3541 internal_error("no text property below deleted line");
3542 return;
3543 }
Bram Moolenaar4b7214e2019-01-03 21:55:32 +01003544 this_props_len = line_size - (int)textlen;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003545 }
3546
3547 found = FALSE;
Bram Moolenaar87be9be2020-05-30 15:32:02 +02003548 for (done_this = 0; done_this < this_props_len;
3549 done_this += sizeof(textprop_T))
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003550 {
Bram Moolenaar87be9be2020-05-30 15:32:02 +02003551 int flag = above ? TP_FLAG_CONT_NEXT
3552 : TP_FLAG_CONT_PREV;
3553 textprop_T prop_this;
3554
3555 mch_memmove(&prop_this, text + textlen + done_del,
3556 sizeof(textprop_T));
3557 if ((prop_this.tp_flags & flag)
3558 && prop_del.tp_id == prop_this.tp_id
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003559 && prop_del.tp_type == prop_this.tp_type)
3560 {
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003561 found = TRUE;
Bram Moolenaar87be9be2020-05-30 15:32:02 +02003562 prop_this.tp_flags &= ~flag;
3563 mch_memmove(text + textlen + done_del, &prop_this,
3564 sizeof(textprop_T));
3565 break;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003566 }
3567 }
3568 if (!found)
3569 {
3570 if (above)
3571 internal_error("text property above deleted line not found");
3572 else
3573 internal_error("text property below deleted line not found");
3574 }
3575
3576 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
3577 }
3578 }
3579}
3580#endif
3581
Bram Moolenaar071d4272004-06-13 20:20:40 +00003582/*
Bram Moolenaar4033c552017-09-16 20:54:51 +02003583 * Delete line "lnum" in the current buffer.
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003584 * When "flags" has ML_DEL_MESSAGE may give a "No lines in buffer" message.
3585 * When "flags" has ML_DEL_UNDO this is called from undo.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003586 *
3587 * return FAIL for failure, OK otherwise
3588 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003589 static int
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003590ml_delete_int(buf_T *buf, linenr_T lnum, int flags)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003591{
3592 bhdr_T *hp;
3593 memfile_T *mfp;
3594 DATA_BL *dp;
3595 PTR_BL *pp;
3596 infoptr_T *ip;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003597 int count; // number of entries in block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003598 int idx;
3599 int stack_idx;
3600 int text_start;
3601 int line_start;
3602 long line_size;
3603 int i;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003604 int ret = FAIL;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003605#ifdef FEAT_PROP_POPUP
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003606 char_u *textprop_save = NULL;
3607 int textprop_save_len;
3608#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003609
Bram Moolenaar071d4272004-06-13 20:20:40 +00003610 if (lowest_marked && lowest_marked > lnum)
3611 lowest_marked--;
3612
3613/*
3614 * If the file becomes empty the last line is replaced by an empty line.
3615 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003616 if (buf->b_ml.ml_line_count == 1) // file becomes empty
Bram Moolenaar071d4272004-06-13 20:20:40 +00003617 {
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003618 if ((flags & ML_DEL_MESSAGE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003619#ifdef FEAT_NETBEANS_INTG
3620 && !netbeansSuppressNoLines
3621#endif
3622 )
Bram Moolenaar238a5642006-02-21 22:12:05 +00003623 set_keep_msg((char_u *)_(no_lines_msg), 0);
3624
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003625 // FEAT_BYTEOFF already handled in there, don't worry 'bout it below
Bram Moolenaar071d4272004-06-13 20:20:40 +00003626 i = ml_replace((linenr_T)1, (char_u *)"", TRUE);
3627 buf->b_ml.ml_flags |= ML_EMPTY;
3628
3629 return i;
3630 }
3631
3632/*
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003633 * Find the data block containing the line.
3634 * This also fills the stack with the blocks from the root to the data block.
3635 * This also releases any locked block..
Bram Moolenaar071d4272004-06-13 20:20:40 +00003636 */
3637 mfp = buf->b_ml.ml_mfp;
3638 if (mfp == NULL)
3639 return FAIL;
3640
3641 if ((hp = ml_find_line(buf, lnum, ML_DELETE)) == NULL)
3642 return FAIL;
3643
3644 dp = (DATA_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003645 // compute line count before the delete
Bram Moolenaar071d4272004-06-13 20:20:40 +00003646 count = (long)(buf->b_ml.ml_locked_high)
3647 - (long)(buf->b_ml.ml_locked_low) + 2;
3648 idx = lnum - buf->b_ml.ml_locked_low;
3649
3650 --buf->b_ml.ml_line_count;
3651
3652 line_start = ((dp->db_index[idx]) & DB_INDEX_MASK);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003653 if (idx == 0) // first line in block, text at the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00003654 line_size = dp->db_txt_end - line_start;
3655 else
3656 line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK) - line_start;
3657
3658#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02003659 if (netbeans_active())
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00003660 netbeans_removed(buf, lnum, 0, (long)line_size);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003661#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003662#ifdef FEAT_PROP_POPUP
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003663 // If there are text properties, make a copy, so that we can update
3664 // properties in preceding and following lines.
Bram Moolenaar4cd5c522021-06-27 13:04:00 +02003665 if (buf->b_has_textprop && !(flags & (ML_DEL_UNDO | ML_DEL_NOPROP)))
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003666 {
3667 size_t textlen = STRLEN((char_u *)dp + line_start) + 1;
3668
3669 if ((long)textlen < line_size)
3670 {
Bram Moolenaar4b7214e2019-01-03 21:55:32 +01003671 textprop_save_len = line_size - (int)textlen;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003672 textprop_save = vim_memsave((char_u *)dp + line_start + textlen,
3673 textprop_save_len);
3674 }
3675 }
3676#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003677
3678/*
3679 * special case: If there is only one line in the data block it becomes empty.
3680 * Then we have to remove the entry, pointing to this data block, from the
3681 * pointer block. If this pointer block also becomes empty, we go up another
3682 * block, and so on, up to the root if necessary.
3683 * The line counts in the pointer blocks have already been adjusted by
3684 * ml_find_line().
3685 */
3686 if (count == 1)
3687 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003688 mf_free(mfp, hp); // free the data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003689 buf->b_ml.ml_locked = NULL;
3690
Bram Moolenaare60acc12011-05-10 16:41:25 +02003691 for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0;
3692 --stack_idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003693 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003694 buf->b_ml.ml_stack_top = 0; // stack is invalid when failing
Bram Moolenaar071d4272004-06-13 20:20:40 +00003695 ip = &(buf->b_ml.ml_stack[stack_idx]);
3696 idx = ip->ip_index;
3697 if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003698 goto theend;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003699 pp = (PTR_BL *)(hp->bh_data); // must be pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003700 if (pp->pb_id != PTR_ID)
3701 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003702 iemsg(_("E317: pointer block id wrong 4"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003703 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003704 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003705 }
3706 count = --(pp->pb_count);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003707 if (count == 0) // the pointer block becomes empty!
Bram Moolenaar071d4272004-06-13 20:20:40 +00003708 mf_free(mfp, hp);
3709 else
3710 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003711 if (count != idx) // move entries after the deleted one
Bram Moolenaar071d4272004-06-13 20:20:40 +00003712 mch_memmove(&pp->pb_pointer[idx], &pp->pb_pointer[idx + 1],
3713 (size_t)(count - idx) * sizeof(PTR_EN));
3714 mf_put(mfp, hp, TRUE, FALSE);
3715
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003716 buf->b_ml.ml_stack_top = stack_idx; // truncate stack
3717 // fix line count for rest of blocks in the stack
Bram Moolenaar6b803a72007-05-06 14:25:46 +00003718 if (buf->b_ml.ml_locked_lineadd != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003719 {
3720 ml_lineadd(buf, buf->b_ml.ml_locked_lineadd);
3721 buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high +=
Bram Moolenaar6b803a72007-05-06 14:25:46 +00003722 buf->b_ml.ml_locked_lineadd;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003723 }
3724 ++(buf->b_ml.ml_stack_top);
3725
3726 break;
3727 }
3728 }
3729 CHECK(stack_idx < 0, _("deleted block 1?"));
3730 }
3731 else
3732 {
3733 /*
3734 * delete the text by moving the next lines forwards
3735 */
3736 text_start = dp->db_txt_start;
3737 mch_memmove((char *)dp + text_start + line_size,
3738 (char *)dp + text_start, (size_t)(line_start - text_start));
3739
3740 /*
3741 * delete the index by moving the next indexes backwards
3742 * Adjust the indexes for the text movement.
3743 */
3744 for (i = idx; i < count - 1; ++i)
3745 dp->db_index[i] = dp->db_index[i + 1] + line_size;
3746
3747 dp->db_free += line_size + INDEX_SIZE;
3748 dp->db_txt_start += line_size;
3749 --(dp->db_line_count);
3750
3751 /*
3752 * mark the block dirty and make sure it is in the file (for recovery)
3753 */
3754 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
3755 }
3756
3757#ifdef FEAT_BYTEOFF
3758 ml_updatechunk(buf, lnum, line_size, ML_CHNK_DELLINE);
3759#endif
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003760 ret = OK;
3761
3762theend:
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003763#ifdef FEAT_PROP_POPUP
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003764 if (textprop_save != NULL)
3765 {
3766 // Adjust text properties in the line above and below.
3767 if (lnum > 1)
Bram Moolenaar4cd5c522021-06-27 13:04:00 +02003768 adjust_text_props_for_delete(buf, lnum - 1, textprop_save,
3769 textprop_save_len, TRUE);
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003770 if (lnum <= buf->b_ml.ml_line_count)
Bram Moolenaar4cd5c522021-06-27 13:04:00 +02003771 adjust_text_props_for_delete(buf, lnum, textprop_save,
3772 textprop_save_len, FALSE);
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003773 }
3774 vim_free(textprop_save);
3775#endif
3776 return ret;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003777}
3778
3779/*
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003780 * Delete line "lnum" in the current buffer.
3781 * When "message" is TRUE may give a "No lines in buffer" message.
3782 *
3783 * Check: The caller of this function should probably also call
3784 * deleted_lines() after this.
3785 *
3786 * return FAIL for failure, OK otherwise
3787 */
3788 int
Bram Moolenaarca70c072020-05-30 20:30:46 +02003789ml_delete(linenr_T lnum)
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003790{
Bram Moolenaarca70c072020-05-30 20:30:46 +02003791 return ml_delete_flags(lnum, 0);
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003792}
3793
3794/*
3795 * Like ml_delete() but using flags (see ml_delete_int()).
3796 */
3797 int
3798ml_delete_flags(linenr_T lnum, int flags)
3799{
3800 ml_flush_line(curbuf);
3801 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count)
3802 return FAIL;
3803
3804#ifdef FEAT_EVAL
3805 // When inserting above recorded changes: flush the changes before changing
3806 // the text.
3807 may_invoke_listeners(curbuf, lnum, lnum + 1, -1);
3808#endif
3809
3810 return ml_delete_int(curbuf, lnum, flags);
3811}
3812
3813/*
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003814 * set the DB_MARKED flag for line 'lnum'
Bram Moolenaar071d4272004-06-13 20:20:40 +00003815 */
3816 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003817ml_setmarked(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003818{
3819 bhdr_T *hp;
3820 DATA_BL *dp;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003821 // invalid line number
Bram Moolenaar071d4272004-06-13 20:20:40 +00003822 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count
3823 || curbuf->b_ml.ml_mfp == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003824 return; // give error message?
Bram Moolenaar071d4272004-06-13 20:20:40 +00003825
3826 if (lowest_marked == 0 || lowest_marked > lnum)
3827 lowest_marked = lnum;
3828
3829 /*
3830 * find the data block containing the line
3831 * This also fills the stack with the blocks from the root to the data block
3832 * This also releases any locked block.
3833 */
3834 if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003835 return; // give error message?
Bram Moolenaar071d4272004-06-13 20:20:40 +00003836
3837 dp = (DATA_BL *)(hp->bh_data);
3838 dp->db_index[lnum - curbuf->b_ml.ml_locked_low] |= DB_MARKED;
3839 curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
3840}
3841
3842/*
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003843 * find the first line with its DB_MARKED flag set
Bram Moolenaar071d4272004-06-13 20:20:40 +00003844 */
3845 linenr_T
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003846ml_firstmarked(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003847{
3848 bhdr_T *hp;
3849 DATA_BL *dp;
3850 linenr_T lnum;
3851 int i;
3852
3853 if (curbuf->b_ml.ml_mfp == NULL)
3854 return (linenr_T) 0;
3855
3856 /*
3857 * The search starts with lowest_marked line. This is the last line where
3858 * a mark was found, adjusted by inserting/deleting lines.
3859 */
3860 for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count; )
3861 {
3862 /*
3863 * Find the data block containing the line.
3864 * This also fills the stack with the blocks from the root to the data
3865 * block This also releases any locked block.
3866 */
3867 if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003868 return (linenr_T)0; // give error message?
Bram Moolenaar071d4272004-06-13 20:20:40 +00003869
3870 dp = (DATA_BL *)(hp->bh_data);
3871
3872 for (i = lnum - curbuf->b_ml.ml_locked_low;
3873 lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum)
3874 if ((dp->db_index[i]) & DB_MARKED)
3875 {
3876 (dp->db_index[i]) &= DB_INDEX_MASK;
3877 curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
3878 lowest_marked = lnum + 1;
3879 return lnum;
3880 }
3881 }
3882
3883 return (linenr_T) 0;
3884}
3885
Bram Moolenaar071d4272004-06-13 20:20:40 +00003886/*
3887 * clear all DB_MARKED flags
3888 */
3889 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003890ml_clearmarked(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003891{
3892 bhdr_T *hp;
3893 DATA_BL *dp;
3894 linenr_T lnum;
3895 int i;
3896
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003897 if (curbuf->b_ml.ml_mfp == NULL) // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00003898 return;
3899
3900 /*
3901 * The search starts with line lowest_marked.
3902 */
3903 for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count; )
3904 {
3905 /*
3906 * Find the data block containing the line.
3907 * This also fills the stack with the blocks from the root to the data
3908 * block and releases any locked block.
3909 */
3910 if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003911 return; // give error message?
Bram Moolenaar071d4272004-06-13 20:20:40 +00003912
3913 dp = (DATA_BL *)(hp->bh_data);
3914
3915 for (i = lnum - curbuf->b_ml.ml_locked_low;
3916 lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum)
3917 if ((dp->db_index[i]) & DB_MARKED)
3918 {
3919 (dp->db_index[i]) &= DB_INDEX_MASK;
3920 curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
3921 }
3922 }
3923
3924 lowest_marked = 0;
3925 return;
3926}
3927
3928/*
3929 * flush ml_line if necessary
3930 */
3931 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003932ml_flush_line(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003933{
3934 bhdr_T *hp;
3935 DATA_BL *dp;
3936 linenr_T lnum;
3937 char_u *new_line;
3938 char_u *old_line;
3939 colnr_T new_len;
3940 int old_len;
3941 int extra;
3942 int idx;
3943 int start;
3944 int count;
3945 int i;
Bram Moolenaar0ca4b352010-02-11 18:54:43 +01003946 static int entered = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003947
3948 if (buf->b_ml.ml_line_lnum == 0 || buf->b_ml.ml_mfp == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003949 return; // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00003950
3951 if (buf->b_ml.ml_flags & ML_LINE_DIRTY)
3952 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003953 // This code doesn't work recursively, but Netbeans may call back here
3954 // when obtaining the cursor position.
Bram Moolenaar0ca4b352010-02-11 18:54:43 +01003955 if (entered)
3956 return;
3957 entered = TRUE;
3958
Bram Moolenaar071d4272004-06-13 20:20:40 +00003959 lnum = buf->b_ml.ml_line_lnum;
3960 new_line = buf->b_ml.ml_line_ptr;
3961
3962 hp = ml_find_line(buf, lnum, ML_FIND);
3963 if (hp == NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003964 siemsg(_("E320: Cannot find line %ld"), lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003965 else
3966 {
3967 dp = (DATA_BL *)(hp->bh_data);
3968 idx = lnum - buf->b_ml.ml_locked_low;
3969 start = ((dp->db_index[idx]) & DB_INDEX_MASK);
3970 old_line = (char_u *)dp + start;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003971 if (idx == 0) // line is last in block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003972 old_len = dp->db_txt_end - start;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003973 else // text of previous line follows
Bram Moolenaar071d4272004-06-13 20:20:40 +00003974 old_len = (dp->db_index[idx - 1] & DB_INDEX_MASK) - start;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003975 new_len = buf->b_ml.ml_line_len;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003976 extra = new_len - old_len; // negative if lines gets smaller
Bram Moolenaar071d4272004-06-13 20:20:40 +00003977
3978 /*
3979 * if new line fits in data block, replace directly
3980 */
3981 if ((int)dp->db_free >= extra)
3982 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003983 // if the length changes and there are following lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00003984 count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 1;
3985 if (extra != 0 && idx < count - 1)
3986 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003987 // move text of following lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00003988 mch_memmove((char *)dp + dp->db_txt_start - extra,
3989 (char *)dp + dp->db_txt_start,
3990 (size_t)(start - dp->db_txt_start));
3991
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003992 // adjust pointers of this and following lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00003993 for (i = idx + 1; i < count; ++i)
3994 dp->db_index[i] -= extra;
3995 }
3996 dp->db_index[idx] -= extra;
3997
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003998 // adjust free space
Bram Moolenaar071d4272004-06-13 20:20:40 +00003999 dp->db_free -= extra;
4000 dp->db_txt_start -= extra;
4001
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004002 // copy new line into the data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00004003 mch_memmove(old_line - extra, new_line, (size_t)new_len);
4004 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
4005#ifdef FEAT_BYTEOFF
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004006 // The else case is already covered by the insert and delete
Bram Moolenaar071d4272004-06-13 20:20:40 +00004007 ml_updatechunk(buf, lnum, (long)extra, ML_CHNK_UPDLINE);
4008#endif
4009 }
4010 else
4011 {
4012 /*
4013 * Cannot do it in one data block: Delete and append.
4014 * Append first, because ml_delete_int() cannot delete the
4015 * last line in a buffer, which causes trouble for a buffer
4016 * that has only one line.
4017 * Don't forget to copy the mark!
4018 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004019 // How about handling errors???
Bram Moolenaara9d4b842020-05-30 14:46:52 +02004020 (void)ml_append_int(buf, lnum, new_line, new_len,
Bram Moolenaar840f91f2021-05-26 22:32:10 +02004021 ((dp->db_index[idx] & DB_MARKED) ? ML_APPEND_MARK : 0)
4022#ifdef FEAT_PROP_POPUP
4023 | ML_APPEND_NOPROP
4024#endif
4025 );
Bram Moolenaar4cd5c522021-06-27 13:04:00 +02004026 (void)ml_delete_int(buf, lnum, ML_DEL_NOPROP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004027 }
4028 }
4029 vim_free(new_line);
Bram Moolenaar0ca4b352010-02-11 18:54:43 +01004030
4031 entered = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004032 }
4033
4034 buf->b_ml.ml_line_lnum = 0;
4035}
4036
4037/*
4038 * create a new, empty, data block
4039 */
4040 static bhdr_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004041ml_new_data(memfile_T *mfp, int negative, int page_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004042{
4043 bhdr_T *hp;
4044 DATA_BL *dp;
4045
4046 if ((hp = mf_new(mfp, negative, page_count)) == NULL)
4047 return NULL;
4048
4049 dp = (DATA_BL *)(hp->bh_data);
4050 dp->db_id = DATA_ID;
4051 dp->db_txt_start = dp->db_txt_end = page_count * mfp->mf_page_size;
4052 dp->db_free = dp->db_txt_start - HEADER_SIZE;
4053 dp->db_line_count = 0;
4054
4055 return hp;
4056}
4057
4058/*
4059 * create a new, empty, pointer block
4060 */
4061 static bhdr_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004062ml_new_ptr(memfile_T *mfp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004063{
4064 bhdr_T *hp;
4065 PTR_BL *pp;
4066
4067 if ((hp = mf_new(mfp, FALSE, 1)) == NULL)
4068 return NULL;
4069
4070 pp = (PTR_BL *)(hp->bh_data);
4071 pp->pb_id = PTR_ID;
4072 pp->pb_count = 0;
Bram Moolenaar20a825a2010-05-31 21:27:30 +02004073 pp->pb_count_max = (short_u)((mfp->mf_page_size - sizeof(PTR_BL))
4074 / sizeof(PTR_EN) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004075
4076 return hp;
4077}
4078
4079/*
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01004080 * Lookup line 'lnum' in a memline.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004081 *
4082 * action: if ML_DELETE or ML_INSERT the line count is updated while searching
4083 * if ML_FLUSH only flush a locked block
4084 * if ML_FIND just find the line
4085 *
4086 * If the block was found it is locked and put in ml_locked.
4087 * The stack is updated to lead to the locked block. The ip_high field in
4088 * the stack is updated to reflect the last line in the block AFTER the
4089 * insert or delete, also if the pointer block has not been updated yet. But
Bram Moolenaar6b803a72007-05-06 14:25:46 +00004090 * if ml_locked != NULL ml_locked_lineadd must be added to ip_high.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004091 *
4092 * return: NULL for failure, pointer to block header otherwise
4093 */
4094 static bhdr_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004095ml_find_line(buf_T *buf, linenr_T lnum, int action)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004096{
4097 DATA_BL *dp;
4098 PTR_BL *pp;
4099 infoptr_T *ip;
4100 bhdr_T *hp;
4101 memfile_T *mfp;
4102 linenr_T t;
4103 blocknr_T bnum, bnum2;
4104 int dirty;
4105 linenr_T low, high;
4106 int top;
4107 int page_count;
4108 int idx;
4109
4110 mfp = buf->b_ml.ml_mfp;
4111
4112 /*
4113 * If there is a locked block check if the wanted line is in it.
4114 * If not, flush and release the locked block.
4115 * Don't do this for ML_INSERT_SAME, because the stack need to be updated.
4116 * Don't do this for ML_FLUSH, because we want to flush the locked block.
Bram Moolenaar47b8b152007-02-07 02:41:57 +00004117 * Don't do this when 'swapfile' is reset, we want to load all the blocks.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004118 */
4119 if (buf->b_ml.ml_locked)
4120 {
Bram Moolenaar47b8b152007-02-07 02:41:57 +00004121 if (ML_SIMPLE(action)
4122 && buf->b_ml.ml_locked_low <= lnum
4123 && buf->b_ml.ml_locked_high >= lnum
4124 && !mf_dont_release)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004125 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004126 // remember to update pointer blocks and stack later
Bram Moolenaar071d4272004-06-13 20:20:40 +00004127 if (action == ML_INSERT)
4128 {
4129 ++(buf->b_ml.ml_locked_lineadd);
4130 ++(buf->b_ml.ml_locked_high);
4131 }
4132 else if (action == ML_DELETE)
4133 {
4134 --(buf->b_ml.ml_locked_lineadd);
4135 --(buf->b_ml.ml_locked_high);
4136 }
4137 return (buf->b_ml.ml_locked);
4138 }
4139
4140 mf_put(mfp, buf->b_ml.ml_locked, buf->b_ml.ml_flags & ML_LOCKED_DIRTY,
4141 buf->b_ml.ml_flags & ML_LOCKED_POS);
4142 buf->b_ml.ml_locked = NULL;
4143
Bram Moolenaar6b803a72007-05-06 14:25:46 +00004144 /*
4145 * If lines have been added or deleted in the locked block, need to
4146 * update the line count in pointer blocks.
4147 */
4148 if (buf->b_ml.ml_locked_lineadd != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004149 ml_lineadd(buf, buf->b_ml.ml_locked_lineadd);
4150 }
4151
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004152 if (action == ML_FLUSH) // nothing else to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00004153 return NULL;
4154
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004155 bnum = 1; // start at the root of the tree
Bram Moolenaar071d4272004-06-13 20:20:40 +00004156 page_count = 1;
4157 low = 1;
4158 high = buf->b_ml.ml_line_count;
4159
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004160 if (action == ML_FIND) // first try stack entries
Bram Moolenaar071d4272004-06-13 20:20:40 +00004161 {
4162 for (top = buf->b_ml.ml_stack_top - 1; top >= 0; --top)
4163 {
4164 ip = &(buf->b_ml.ml_stack[top]);
4165 if (ip->ip_low <= lnum && ip->ip_high >= lnum)
4166 {
4167 bnum = ip->ip_bnum;
4168 low = ip->ip_low;
4169 high = ip->ip_high;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004170 buf->b_ml.ml_stack_top = top; // truncate stack at prev entry
Bram Moolenaar071d4272004-06-13 20:20:40 +00004171 break;
4172 }
4173 }
4174 if (top < 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004175 buf->b_ml.ml_stack_top = 0; // not found, start at the root
Bram Moolenaar071d4272004-06-13 20:20:40 +00004176 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004177 else // ML_DELETE or ML_INSERT
4178 buf->b_ml.ml_stack_top = 0; // start at the root
Bram Moolenaar071d4272004-06-13 20:20:40 +00004179
4180/*
4181 * search downwards in the tree until a data block is found
4182 */
4183 for (;;)
4184 {
4185 if ((hp = mf_get(mfp, bnum, page_count)) == NULL)
4186 goto error_noblock;
4187
4188 /*
4189 * update high for insert/delete
4190 */
4191 if (action == ML_INSERT)
4192 ++high;
4193 else if (action == ML_DELETE)
4194 --high;
4195
4196 dp = (DATA_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004197 if (dp->db_id == DATA_ID) // data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00004198 {
4199 buf->b_ml.ml_locked = hp;
4200 buf->b_ml.ml_locked_low = low;
4201 buf->b_ml.ml_locked_high = high;
4202 buf->b_ml.ml_locked_lineadd = 0;
4203 buf->b_ml.ml_flags &= ~(ML_LOCKED_DIRTY | ML_LOCKED_POS);
4204 return hp;
4205 }
4206
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004207 pp = (PTR_BL *)(dp); // must be pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00004208 if (pp->pb_id != PTR_ID)
4209 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004210 iemsg(_("E317: pointer block id wrong"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004211 goto error_block;
4212 }
4213
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004214 if ((top = ml_add_stack(buf)) < 0) // add new entry to stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00004215 goto error_block;
4216 ip = &(buf->b_ml.ml_stack[top]);
4217 ip->ip_bnum = bnum;
4218 ip->ip_low = low;
4219 ip->ip_high = high;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004220 ip->ip_index = -1; // index not known yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00004221
4222 dirty = FALSE;
4223 for (idx = 0; idx < (int)pp->pb_count; ++idx)
4224 {
4225 t = pp->pb_pointer[idx].pe_line_count;
4226 CHECK(t == 0, _("pe_line_count is zero"));
4227 if ((low += t) > lnum)
4228 {
4229 ip->ip_index = idx;
4230 bnum = pp->pb_pointer[idx].pe_bnum;
4231 page_count = pp->pb_pointer[idx].pe_page_count;
4232 high = low - 1;
4233 low -= t;
4234
4235 /*
4236 * a negative block number may have been changed
4237 */
4238 if (bnum < 0)
4239 {
4240 bnum2 = mf_trans_del(mfp, bnum);
4241 if (bnum != bnum2)
4242 {
4243 bnum = bnum2;
4244 pp->pb_pointer[idx].pe_bnum = bnum;
4245 dirty = TRUE;
4246 }
4247 }
4248
4249 break;
4250 }
4251 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004252 if (idx >= (int)pp->pb_count) // past the end: something wrong!
Bram Moolenaar071d4272004-06-13 20:20:40 +00004253 {
4254 if (lnum > buf->b_ml.ml_line_count)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004255 siemsg(_("E322: line number out of range: %ld past the end"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00004256 lnum - buf->b_ml.ml_line_count);
4257
4258 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004259 siemsg(_("E323: line count wrong in block %ld"), bnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004260 goto error_block;
4261 }
4262 if (action == ML_DELETE)
4263 {
4264 pp->pb_pointer[idx].pe_line_count--;
4265 dirty = TRUE;
4266 }
4267 else if (action == ML_INSERT)
4268 {
4269 pp->pb_pointer[idx].pe_line_count++;
4270 dirty = TRUE;
4271 }
4272 mf_put(mfp, hp, dirty, FALSE);
4273 }
4274
4275error_block:
4276 mf_put(mfp, hp, FALSE, FALSE);
4277error_noblock:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02004278 /*
4279 * If action is ML_DELETE or ML_INSERT we have to correct the tree for
4280 * the incremented/decremented line counts, because there won't be a line
4281 * inserted/deleted after all.
4282 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004283 if (action == ML_DELETE)
4284 ml_lineadd(buf, 1);
4285 else if (action == ML_INSERT)
4286 ml_lineadd(buf, -1);
4287 buf->b_ml.ml_stack_top = 0;
4288 return NULL;
4289}
4290
4291/*
4292 * add an entry to the info pointer stack
4293 *
4294 * return -1 for failure, number of the new entry otherwise
4295 */
4296 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004297ml_add_stack(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004298{
4299 int top;
4300 infoptr_T *newstack;
4301
4302 top = buf->b_ml.ml_stack_top;
4303
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004304 // may have to increase the stack size
Bram Moolenaar071d4272004-06-13 20:20:40 +00004305 if (top == buf->b_ml.ml_stack_size)
4306 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004307 CHECK(top > 0, _("Stack size increases")); // more than 5 levels???
Bram Moolenaar071d4272004-06-13 20:20:40 +00004308
Bram Moolenaarc799fe22019-05-28 23:08:19 +02004309 newstack = ALLOC_MULT(infoptr_T, buf->b_ml.ml_stack_size + STACK_INCR);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004310 if (newstack == NULL)
4311 return -1;
Bram Moolenaarfbd302f2015-08-08 18:23:46 +02004312 if (top > 0)
4313 mch_memmove(newstack, buf->b_ml.ml_stack,
Bram Moolenaar8c8de832008-06-24 22:58:06 +00004314 (size_t)top * sizeof(infoptr_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004315 vim_free(buf->b_ml.ml_stack);
4316 buf->b_ml.ml_stack = newstack;
4317 buf->b_ml.ml_stack_size += STACK_INCR;
4318 }
4319
4320 buf->b_ml.ml_stack_top++;
4321 return top;
4322}
4323
4324/*
4325 * Update the pointer blocks on the stack for inserted/deleted lines.
4326 * The stack itself is also updated.
4327 *
4328 * When a insert/delete line action fails, the line is not inserted/deleted,
4329 * but the pointer blocks have already been updated. That is fixed here by
4330 * walking through the stack.
4331 *
4332 * Count is the number of lines added, negative if lines have been deleted.
4333 */
4334 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004335ml_lineadd(buf_T *buf, int count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004336{
4337 int idx;
4338 infoptr_T *ip;
4339 PTR_BL *pp;
4340 memfile_T *mfp = buf->b_ml.ml_mfp;
4341 bhdr_T *hp;
4342
4343 for (idx = buf->b_ml.ml_stack_top - 1; idx >= 0; --idx)
4344 {
4345 ip = &(buf->b_ml.ml_stack[idx]);
4346 if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
4347 break;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004348 pp = (PTR_BL *)(hp->bh_data); // must be pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00004349 if (pp->pb_id != PTR_ID)
4350 {
4351 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004352 iemsg(_("E317: pointer block id wrong 2"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004353 break;
4354 }
4355 pp->pb_pointer[ip->ip_index].pe_line_count += count;
4356 ip->ip_high += count;
4357 mf_put(mfp, hp, TRUE, FALSE);
4358 }
4359}
4360
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004361#if defined(HAVE_READLINK) || defined(PROTO)
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004362/*
4363 * Resolve a symlink in the last component of a file name.
4364 * Note that f_resolve() does it for every part of the path, we don't do that
4365 * here.
4366 * If it worked returns OK and the resolved link in "buf[MAXPATHL]".
4367 * Otherwise returns FAIL.
4368 */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004369 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004370resolve_symlink(char_u *fname, char_u *buf)
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004371{
4372 char_u tmp[MAXPATHL];
4373 int ret;
4374 int depth = 0;
4375
4376 if (fname == NULL)
4377 return FAIL;
4378
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004379 // Put the result so far in tmp[], starting with the original name.
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004380 vim_strncpy(tmp, fname, MAXPATHL - 1);
4381
4382 for (;;)
4383 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004384 // Limit symlink depth to 100, catch recursive loops.
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004385 if (++depth == 100)
4386 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004387 semsg(_("E773: Symlink loop for \"%s\""), fname);
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004388 return FAIL;
4389 }
4390
4391 ret = readlink((char *)tmp, (char *)buf, MAXPATHL - 1);
4392 if (ret <= 0)
4393 {
Bram Moolenaarcc984262005-12-23 22:19:46 +00004394 if (errno == EINVAL || errno == ENOENT)
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004395 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004396 // Found non-symlink or not existing file, stop here.
4397 // When at the first level use the unmodified name, skip the
4398 // call to vim_FullName().
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004399 if (depth == 1)
4400 return FAIL;
4401
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004402 // Use the resolved name in tmp[].
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004403 break;
4404 }
4405
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004406 // There must be some error reading links, use original name.
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004407 return FAIL;
4408 }
4409 buf[ret] = NUL;
4410
4411 /*
4412 * Check whether the symlink is relative or absolute.
4413 * If it's relative, build a new path based on the directory
4414 * portion of the filename (if any) and the path the symlink
4415 * points to.
4416 */
4417 if (mch_isFullName(buf))
4418 STRCPY(tmp, buf);
4419 else
4420 {
4421 char_u *tail;
4422
4423 tail = gettail(tmp);
4424 if (STRLEN(tail) + STRLEN(buf) >= MAXPATHL)
4425 return FAIL;
4426 STRCPY(tail, buf);
4427 }
4428 }
4429
4430 /*
4431 * Try to resolve the full name of the file so that the swapfile name will
4432 * be consistent even when opening a relative symlink from different
4433 * working directories.
4434 */
4435 return vim_FullName(tmp, buf, MAXPATHL, TRUE);
4436}
4437#endif
4438
Bram Moolenaar071d4272004-06-13 20:20:40 +00004439/*
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004440 * Make swap file name out of the file name and a directory name.
4441 * Returns pointer to allocated memory or NULL.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004442 */
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004443 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004444makeswapname(
4445 char_u *fname,
4446 char_u *ffname UNUSED,
4447 buf_T *buf,
4448 char_u *dir_name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004449{
4450 char_u *r, *s;
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02004451 char_u *fname_res = fname;
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004452#ifdef HAVE_READLINK
4453 char_u fname_buf[MAXPATHL];
Bram Moolenaar5966ea12020-07-15 15:30:05 +02004454
4455 // Expand symlink in the file name, so that we put the swap file with the
4456 // actual file instead of with the symlink.
4457 if (resolve_symlink(fname, fname_buf) == OK)
4458 fname_res = fname_buf;
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004459#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004460
Bram Moolenaar4f974752019-02-17 17:44:42 +01004461#if defined(UNIX) || defined(MSWIN) // Need _very_ long file names
Bram Moolenaarb113c3a2017-02-28 21:26:17 +01004462 int len = (int)STRLEN(dir_name);
Bram Moolenaarc525e3a2017-02-18 16:59:02 +01004463
4464 s = dir_name + len;
4465 if (after_pathsep(dir_name, s) && len > 1 && s[-1] == s[-2])
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004466 { // Ends with '//', Use Full path
Bram Moolenaar071d4272004-06-13 20:20:40 +00004467 r = NULL;
Bram Moolenaar5966ea12020-07-15 15:30:05 +02004468 if ((s = make_percent_swname(dir_name, fname_res)) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004469 {
4470 r = modname(s, (char_u *)".swp", FALSE);
4471 vim_free(s);
4472 }
4473 return r;
4474 }
4475#endif
4476
4477 r = buf_modname(
Bram Moolenaar071d4272004-06-13 20:20:40 +00004478 (buf->b_p_sn || buf->b_shortname),
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004479 fname_res,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004480 (char_u *)
Bram Moolenaare60acc12011-05-10 16:41:25 +02004481#if defined(VMS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004482 "_swp",
4483#else
4484 ".swp",
4485#endif
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004486 // Prepend a '.' to the swap file name for the current directory.
Bram Moolenaar48e330a2016-02-23 14:53:34 +01004487 dir_name[0] == '.' && dir_name[1] == NUL);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004488 if (r == NULL) // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00004489 return NULL;
4490
4491 s = get_file_in_dir(r, dir_name);
4492 vim_free(r);
4493 return s;
4494}
4495
4496/*
4497 * Get file name to use for swap file or backup file.
4498 * Use the name of the edited file "fname" and an entry in the 'dir' or 'bdir'
4499 * option "dname".
4500 * - If "dname" is ".", return "fname" (swap file in dir of file).
4501 * - If "dname" starts with "./", insert "dname" in "fname" (swap file
4502 * relative to dir of file).
4503 * - Otherwise, prepend "dname" to the tail of "fname" (swap file in specific
4504 * dir).
4505 *
4506 * The return value is an allocated string and can be NULL.
4507 */
4508 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004509get_file_in_dir(
4510 char_u *fname,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004511 char_u *dname) // don't use "dirname", it is a global for Alpha
Bram Moolenaar071d4272004-06-13 20:20:40 +00004512{
4513 char_u *t;
4514 char_u *tail;
4515 char_u *retval;
4516 int save_char;
4517
4518 tail = gettail(fname);
4519
4520 if (dname[0] == '.' && dname[1] == NUL)
4521 retval = vim_strsave(fname);
4522 else if (dname[0] == '.' && vim_ispathsep(dname[1]))
4523 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004524 if (tail == fname) // no path before file name
Bram Moolenaar071d4272004-06-13 20:20:40 +00004525 retval = concat_fnames(dname + 2, tail, TRUE);
4526 else
4527 {
4528 save_char = *tail;
4529 *tail = NUL;
4530 t = concat_fnames(fname, dname + 2, TRUE);
4531 *tail = save_char;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004532 if (t == NULL) // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00004533 retval = NULL;
4534 else
4535 {
4536 retval = concat_fnames(t, tail, TRUE);
4537 vim_free(t);
4538 }
4539 }
4540 }
4541 else
4542 retval = concat_fnames(dname, tail, TRUE);
4543
Bram Moolenaar4f974752019-02-17 17:44:42 +01004544#ifdef MSWIN
Bram Moolenaar69c35002013-11-04 02:54:12 +01004545 if (retval != NULL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01004546 for (t = gettail(retval); *t != NUL; MB_PTR_ADV(t))
Bram Moolenaar69c35002013-11-04 02:54:12 +01004547 if (*t == ':')
4548 *t = '%';
4549#endif
4550
Bram Moolenaar071d4272004-06-13 20:20:40 +00004551 return retval;
4552}
4553
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004554/*
4555 * Print the ATTENTION message: info about an existing swap file.
4556 */
4557 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004558attention_message(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004559 buf_T *buf, // buffer being edited
4560 char_u *fname) // swap file name
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004561{
Bram Moolenaar8767f522016-07-01 17:17:39 +02004562 stat_T st;
Bram Moolenaar63d25552019-05-10 21:28:38 +02004563 time_t swap_mtime;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004564
4565 ++no_wait_return;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004566 (void)emsg(_("E325: ATTENTION"));
Bram Moolenaar32526b32019-01-19 17:43:09 +01004567 msg_puts(_("\nFound a swap file by the name \""));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004568 msg_home_replace(fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004569 msg_puts("\"\n");
Bram Moolenaar63d25552019-05-10 21:28:38 +02004570 swap_mtime = swapfile_info(fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004571 msg_puts(_("While opening file \""));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004572 msg_outtrans(buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004573 msg_puts("\"\n");
Bram Moolenaard6105cb2018-10-13 19:06:27 +02004574 if (mch_stat((char *)buf->b_fname, &st) == -1)
4575 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004576 msg_puts(_(" CANNOT BE FOUND"));
Bram Moolenaard6105cb2018-10-13 19:06:27 +02004577 }
4578 else
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004579 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004580 msg_puts(_(" dated: "));
Bram Moolenaar63d25552019-05-10 21:28:38 +02004581 msg_puts(get_ctime(st.st_mtime, TRUE));
4582 if (swap_mtime != 0 && st.st_mtime > swap_mtime)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004583 msg_puts(_(" NEWER than swap file!\n"));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004584 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004585 // Some of these messages are long to allow translation to
4586 // other languages.
Bram Moolenaar32526b32019-01-19 17:43:09 +01004587 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"));
4588 msg_puts(_("(2) An edit session for this file crashed.\n"));
4589 msg_puts(_(" If this is the case, use \":recover\" or \"vim -r "));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004590 msg_outtrans(buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004591 msg_puts(_("\"\n to recover the changes (see \":help recovery\").\n"));
4592 msg_puts(_(" If you did this already, delete the swap file \""));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004593 msg_outtrans(fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004594 msg_puts(_("\"\n to avoid this message.\n"));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004595 cmdline_row = msg_row;
4596 --no_wait_return;
4597}
4598
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01004599#if defined(FEAT_EVAL)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004600/*
4601 * Trigger the SwapExists autocommands.
4602 * Returns a value for equivalent to do_dialog() (see below):
4603 * 0: still need to ask for a choice
4604 * 1: open read-only
4605 * 2: edit anyway
4606 * 3: recover
4607 * 4: delete it
4608 * 5: quit
4609 * 6: abort
4610 */
4611 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004612do_swapexists(buf_T *buf, char_u *fname)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004613{
4614 set_vim_var_string(VV_SWAPNAME, fname, -1);
4615 set_vim_var_string(VV_SWAPCHOICE, NULL, -1);
4616
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004617 // Trigger SwapExists autocommands with <afile> set to the file being
4618 // edited. Disallow changing directory here.
Bram Moolenaar12c22ce2009-04-22 13:58:46 +00004619 ++allbuf_lock;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004620 apply_autocmds(EVENT_SWAPEXISTS, buf->b_fname, NULL, FALSE, NULL);
Bram Moolenaar12c22ce2009-04-22 13:58:46 +00004621 --allbuf_lock;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004622
4623 set_vim_var_string(VV_SWAPNAME, NULL, -1);
4624
4625 switch (*get_vim_var_str(VV_SWAPCHOICE))
4626 {
4627 case 'o': return 1;
4628 case 'e': return 2;
4629 case 'r': return 3;
4630 case 'd': return 4;
4631 case 'q': return 5;
4632 case 'a': return 6;
4633 }
4634
4635 return 0;
4636}
4637#endif
4638
Bram Moolenaar071d4272004-06-13 20:20:40 +00004639/*
4640 * Find out what name to use for the swap file for buffer 'buf'.
4641 *
4642 * Several names are tried to find one that does not exist
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004643 * Returns the name in allocated memory or NULL.
Bram Moolenaarf541c362011-10-26 11:44:18 +02004644 * When out of memory "dirp" is set to NULL.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004645 *
4646 * Note: If BASENAMELEN is not correct, you will get error messages for
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004647 * not being able to open the swap or undo file
Bram Moolenaar12c22ce2009-04-22 13:58:46 +00004648 * Note: May trigger SwapExists autocmd, pointers may change!
Bram Moolenaar071d4272004-06-13 20:20:40 +00004649 */
4650 static char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004651findswapname(
4652 buf_T *buf,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004653 char_u **dirp, // pointer to list of directories
4654 char_u *old_fname) // don't give warning for this file name
Bram Moolenaar071d4272004-06-13 20:20:40 +00004655{
4656 char_u *fname;
4657 int n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004658 char_u *dir_name;
4659#ifdef AMIGA
4660 BPTR fh;
4661#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004662 int r;
Bram Moolenaar69c35002013-11-04 02:54:12 +01004663 char_u *buf_fname = buf->b_fname;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004664
Bram Moolenaar48e330a2016-02-23 14:53:34 +01004665#if !defined(UNIX)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004666# define CREATE_DUMMY_FILE
4667 FILE *dummyfd = NULL;
4668
Bram Moolenaar4f974752019-02-17 17:44:42 +01004669# ifdef MSWIN
Bram Moolenaar69c35002013-11-04 02:54:12 +01004670 if (buf_fname != NULL && !mch_isFullName(buf_fname)
4671 && vim_strchr(gettail(buf_fname), ':'))
4672 {
4673 char_u *t;
4674
4675 buf_fname = vim_strsave(buf_fname);
4676 if (buf_fname == NULL)
4677 buf_fname = buf->b_fname;
4678 else
Bram Moolenaar91acfff2017-03-12 19:22:36 +01004679 for (t = gettail(buf_fname); *t != NUL; MB_PTR_ADV(t))
Bram Moolenaar69c35002013-11-04 02:54:12 +01004680 if (*t == ':')
4681 *t = '%';
4682 }
4683# endif
4684
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004685 /*
4686 * If we start editing a new file, e.g. "test.doc", which resides on an
4687 * MSDOS compatible filesystem, it is possible that the file
4688 * "test.doc.swp" which we create will be exactly the same file. To avoid
4689 * this problem we temporarily create "test.doc". Don't do this when the
4690 * check below for a 8.3 file name is used.
4691 */
Bram Moolenaar69c35002013-11-04 02:54:12 +01004692 if (!(buf->b_p_sn || buf->b_shortname) && buf_fname != NULL
4693 && mch_getperm(buf_fname) < 0)
4694 dummyfd = mch_fopen((char *)buf_fname, "w");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004695#endif
4696
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004697 /*
4698 * Isolate a directory name from *dirp and put it in dir_name.
4699 * First allocate some memory to put the directory name in.
4700 */
Bram Moolenaar964b3742019-05-24 18:54:09 +02004701 dir_name = alloc(STRLEN(*dirp) + 1);
Bram Moolenaarf541c362011-10-26 11:44:18 +02004702 if (dir_name == NULL)
4703 *dirp = NULL;
4704 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004705 (void)copy_option_part(dirp, dir_name, 31000, ",");
4706
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004707 /*
4708 * we try different names until we find one that does not exist yet
4709 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004710 if (dir_name == NULL) // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00004711 fname = NULL;
4712 else
Bram Moolenaar69c35002013-11-04 02:54:12 +01004713 fname = makeswapname(buf_fname, buf->b_ffname, buf, dir_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004714
4715 for (;;)
4716 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004717 if (fname == NULL) // must be out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00004718 break;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004719 if ((n = (int)STRLEN(fname)) == 0) // safety check
Bram Moolenaar071d4272004-06-13 20:20:40 +00004720 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01004721 VIM_CLEAR(fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004722 break;
4723 }
Bram Moolenaar48e330a2016-02-23 14:53:34 +01004724#if defined(UNIX)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004725/*
4726 * Some systems have a MS-DOS compatible filesystem that use 8.3 character
4727 * file names. If this is the first try and the swap file name does not fit in
4728 * 8.3, detect if this is the case, set shortname and try again.
4729 */
4730 if (fname[n - 2] == 'w' && fname[n - 1] == 'p'
4731 && !(buf->b_p_sn || buf->b_shortname))
4732 {
4733 char_u *tail;
4734 char_u *fname2;
Bram Moolenaar8767f522016-07-01 17:17:39 +02004735 stat_T s1, s2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004736 int f1, f2;
4737 int created1 = FALSE, created2 = FALSE;
4738 int same = FALSE;
4739
4740 /*
4741 * Check if swapfile name does not fit in 8.3:
4742 * It either contains two dots, is longer than 8 chars, or starts
4743 * with a dot.
4744 */
Bram Moolenaar69c35002013-11-04 02:54:12 +01004745 tail = gettail(buf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004746 if ( vim_strchr(tail, '.') != NULL
4747 || STRLEN(tail) > (size_t)8
4748 || *gettail(fname) == '.')
4749 {
4750 fname2 = alloc(n + 2);
4751 if (fname2 != NULL)
4752 {
4753 STRCPY(fname2, fname);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004754 // if fname == "xx.xx.swp", fname2 = "xx.xx.swx"
4755 // if fname == ".xx.swp", fname2 = ".xx.swpx"
4756 // if fname == "123456789.swp", fname2 = "12345678x.swp"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004757 if (vim_strchr(tail, '.') != NULL)
4758 fname2[n - 1] = 'x';
4759 else if (*gettail(fname) == '.')
4760 {
4761 fname2[n] = 'x';
4762 fname2[n + 1] = NUL;
4763 }
4764 else
4765 fname2[n - 5] += 1;
4766 /*
4767 * may need to create the files to be able to use mch_stat()
4768 */
4769 f1 = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
4770 if (f1 < 0)
4771 {
4772 f1 = mch_open_rw((char *)fname,
4773 O_RDWR|O_CREAT|O_EXCL|O_EXTRA);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004774 created1 = TRUE;
4775 }
4776 if (f1 >= 0)
4777 {
4778 f2 = mch_open((char *)fname2, O_RDONLY | O_EXTRA, 0);
4779 if (f2 < 0)
4780 {
4781 f2 = mch_open_rw((char *)fname2,
4782 O_RDWR|O_CREAT|O_EXCL|O_EXTRA);
4783 created2 = TRUE;
4784 }
4785 if (f2 >= 0)
4786 {
4787 /*
4788 * Both files exist now. If mch_stat() returns the
4789 * same device and inode they are the same file.
4790 */
4791 if (mch_fstat(f1, &s1) != -1
4792 && mch_fstat(f2, &s2) != -1
4793 && s1.st_dev == s2.st_dev
4794 && s1.st_ino == s2.st_ino)
4795 same = TRUE;
4796 close(f2);
4797 if (created2)
4798 mch_remove(fname2);
4799 }
4800 close(f1);
4801 if (created1)
4802 mch_remove(fname);
4803 }
4804 vim_free(fname2);
4805 if (same)
4806 {
4807 buf->b_shortname = TRUE;
4808 vim_free(fname);
Bram Moolenaar69c35002013-11-04 02:54:12 +01004809 fname = makeswapname(buf_fname, buf->b_ffname,
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004810 buf, dir_name);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004811 continue; // try again with b_shortname set
Bram Moolenaar071d4272004-06-13 20:20:40 +00004812 }
4813 }
4814 }
4815 }
4816#endif
4817 /*
4818 * check if the swapfile already exists
4819 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004820 if (mch_getperm(fname) < 0) // it does not exist
Bram Moolenaar071d4272004-06-13 20:20:40 +00004821 {
4822#ifdef HAVE_LSTAT
Bram Moolenaar8767f522016-07-01 17:17:39 +02004823 stat_T sb;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004824
4825 /*
4826 * Extra security check: When a swap file is a symbolic link, this
4827 * is most likely a symlink attack.
4828 */
4829 if (mch_lstat((char *)fname, &sb) < 0)
4830#else
4831# ifdef AMIGA
4832 fh = Open((UBYTE *)fname, (long)MODE_NEWFILE);
4833 /*
4834 * on the Amiga mch_getperm() will return -1 when the file exists
4835 * but is being used by another program. This happens if you edit
4836 * a file twice.
4837 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004838 if (fh != (BPTR)NULL) // can open file, OK
Bram Moolenaar071d4272004-06-13 20:20:40 +00004839 {
4840 Close(fh);
4841 mch_remove(fname);
4842 break;
4843 }
4844 if (IoErr() != ERROR_OBJECT_IN_USE
4845 && IoErr() != ERROR_OBJECT_EXISTS)
4846# endif
4847#endif
4848 break;
4849 }
4850
4851 /*
4852 * A file name equal to old_fname is OK to use.
4853 */
4854 if (old_fname != NULL && fnamecmp(fname, old_fname) == 0)
4855 break;
4856
4857 /*
4858 * get here when file already exists
4859 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004860 if (fname[n - 2] == 'w' && fname[n - 1] == 'p') // first try
Bram Moolenaar071d4272004-06-13 20:20:40 +00004861 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00004862 /*
4863 * on MS-DOS compatible filesystems (e.g. messydos) file.doc.swp
4864 * and file.doc are the same file. To guess if this problem is
4865 * present try if file.doc.swx exists. If it does, we set
4866 * buf->b_shortname and try file_doc.swp (dots replaced by
4867 * underscores for this file), and try again. If it doesn't we
4868 * assume that "file.doc.swp" already exists.
4869 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004870 if (!(buf->b_p_sn || buf->b_shortname)) // not tried yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00004871 {
4872 fname[n - 1] = 'x';
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004873 r = mch_getperm(fname); // try "file.swx"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004874 fname[n - 1] = 'p';
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004875 if (r >= 0) // "file.swx" seems to exist
Bram Moolenaar071d4272004-06-13 20:20:40 +00004876 {
4877 buf->b_shortname = TRUE;
4878 vim_free(fname);
Bram Moolenaar69c35002013-11-04 02:54:12 +01004879 fname = makeswapname(buf_fname, buf->b_ffname,
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004880 buf, dir_name);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004881 continue; // try again with '.' replaced with '_'
Bram Moolenaar071d4272004-06-13 20:20:40 +00004882 }
4883 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004884 /*
4885 * If we get here the ".swp" file really exists.
4886 * Give an error message, unless recovering, no file name, we are
4887 * viewing a help file or when the path of the file is different
4888 * (happens when all .swp files are in one directory).
4889 */
Bram Moolenaar69c35002013-11-04 02:54:12 +01004890 if (!recoverymode && buf_fname != NULL
Bram Moolenaar2debf1c2019-08-04 20:44:19 +02004891 && !buf->b_help
4892 && !(buf->b_flags & (BF_DUMMY | BF_NO_SEA)))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004893 {
4894 int fd;
4895 struct block0 b0;
4896 int differ = FALSE;
4897
4898 /*
4899 * Try to read block 0 from the swap file to get the original
4900 * file name (and inode number).
4901 */
4902 fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
4903 if (fd >= 0)
4904 {
Bram Moolenaar540fc6f2010-12-17 16:27:16 +01004905 if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004906 {
4907 /*
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004908 * If the swapfile has the same directory as the
4909 * buffer don't compare the directory names, they can
4910 * have a different mountpoint.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004911 */
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004912 if (b0.b0_flags & B0_SAME_DIR)
4913 {
4914 if (fnamecmp(gettail(buf->b_ffname),
4915 gettail(b0.b0_fname)) != 0
4916 || !same_directory(fname, buf->b_ffname))
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004917 {
4918#ifdef CHECK_INODE
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004919 // Symlinks may point to the same file even
4920 // when the name differs, need to check the
4921 // inode too.
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004922 expand_env(b0.b0_fname, NameBuff, MAXPATHL);
4923 if (fnamecmp_ino(buf->b_ffname, NameBuff,
4924 char_to_long(b0.b0_ino)))
4925#endif
4926 differ = TRUE;
4927 }
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004928 }
4929 else
4930 {
4931 /*
4932 * The name in the swap file may be
4933 * "~user/path/file". Expand it first.
4934 */
4935 expand_env(b0.b0_fname, NameBuff, MAXPATHL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004936#ifdef CHECK_INODE
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004937 if (fnamecmp_ino(buf->b_ffname, NameBuff,
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004938 char_to_long(b0.b0_ino)))
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004939 differ = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004940#else
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004941 if (fnamecmp(NameBuff, buf->b_ffname) != 0)
4942 differ = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004943#endif
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004944 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004945 }
4946 close(fd);
4947 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004948
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004949 // give the ATTENTION message when there is an old swap file
4950 // for the current file, and the buffer was not recovered.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004951 if (differ == FALSE && !(curbuf->b_flags & BF_RECOVERED)
4952 && vim_strchr(p_shm, SHM_ATTENTION) == NULL)
4953 {
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004954 int choice = 0;
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02004955 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004956#ifdef CREATE_DUMMY_FILE
4957 int did_use_dummy = FALSE;
4958
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004959 // Avoid getting a warning for the file being created
4960 // outside of Vim, it was created at the start of this
4961 // function. Delete the file now, because Vim might exit
4962 // here if the window is closed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004963 if (dummyfd != NULL)
4964 {
4965 fclose(dummyfd);
4966 dummyfd = NULL;
Bram Moolenaar69c35002013-11-04 02:54:12 +01004967 mch_remove(buf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004968 did_use_dummy = TRUE;
4969 }
4970#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004971
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02004972#ifdef HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar071d4272004-06-13 20:20:40 +00004973 process_still_running = FALSE;
4974#endif
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02004975 // It's safe to delete the swap file if all these are true:
4976 // - the edited file exists
4977 // - the swap file has no changes and looks OK
4978 if (mch_stat((char *)buf->b_fname, &st) == 0
4979 && swapfile_unchanged(fname))
4980 {
4981 choice = 4;
4982 if (p_verbose > 0)
4983 verb_msg(_("Found a swap file that is not useful, deleting it"));
4984 }
4985
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01004986#if defined(FEAT_EVAL)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004987 /*
4988 * If there is an SwapExists autocommand and we can handle
4989 * the response, trigger it. It may return 0 to ask the
4990 * user anyway.
4991 */
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02004992 if (choice == 0
4993 && swap_exists_action != SEA_NONE
Bram Moolenaar69c35002013-11-04 02:54:12 +01004994 && has_autocmd(EVENT_SWAPEXISTS, buf_fname, buf))
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004995 choice = do_swapexists(buf, fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004996
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004997 if (choice == 0)
4998#endif
4999 {
5000#ifdef FEAT_GUI
Bram Moolenaar798184c2018-10-07 20:48:39 +02005001 // If we are supposed to start the GUI but it wasn't
5002 // completely started yet, start it now. This makes
5003 // the messages displayed in the Vim window when
5004 // loading a session from the .gvimrc file.
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005005 if (gui.starting && !gui.in_use)
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005006 gui_start(NULL);
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005007#endif
Bram Moolenaar798184c2018-10-07 20:48:39 +02005008 // Show info about the existing swap file.
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005009 attention_message(buf, fname);
5010
Bram Moolenaar798184c2018-10-07 20:48:39 +02005011 // We don't want a 'q' typed at the more-prompt
5012 // interrupt loading a file.
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005013 got_int = FALSE;
Bram Moolenaar798184c2018-10-07 20:48:39 +02005014
5015 // If vimrc has "simalt ~x" we don't want it to
5016 // interfere with the prompt here.
Bram Moolenaar6a2633b2018-10-07 23:16:36 +02005017 flush_buffers(FLUSH_TYPEAHEAD);
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005018 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005019
5020#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005021 if (swap_exists_action != SEA_NONE && choice == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005022 {
5023 char_u *name;
5024
Bram Moolenaar964b3742019-05-24 18:54:09 +02005025 name = alloc(STRLEN(fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005026 + STRLEN(_("Swap file \""))
Bram Moolenaar964b3742019-05-24 18:54:09 +02005027 + STRLEN(_("\" already exists!")) + 5);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005028 if (name != NULL)
5029 {
5030 STRCPY(name, _("Swap file \""));
5031 home_replace(NULL, fname, name + STRLEN(name),
5032 1000, TRUE);
5033 STRCAT(name, _("\" already exists!"));
5034 }
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005035 choice = do_dialog(VIM_WARNING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005036 (char_u *)_("VIM - ATTENTION"),
5037 name == NULL
5038 ? (char_u *)_("Swap file already exists!")
5039 : name,
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02005040# ifdef HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar071d4272004-06-13 20:20:40 +00005041 process_still_running
5042 ? (char_u *)_("&Open Read-Only\n&Edit anyway\n&Recover\n&Quit\n&Abort") :
5043# endif
Bram Moolenaard2c340a2011-01-17 20:08:11 +01005044 (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 +00005045
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02005046# ifdef HAVE_PROCESS_STILL_RUNNING
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005047 if (process_still_running && choice >= 4)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005048 choice++; // Skip missing "Delete it" button
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005049# endif
5050 vim_free(name);
5051
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005052 // pretend screen didn't scroll, need redraw anyway
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005053 msg_scrolled = 0;
5054 redraw_all_later(NOT_VALID);
5055 }
5056#endif
5057
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005058 if (choice > 0)
5059 {
5060 switch (choice)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005061 {
5062 case 1:
5063 buf->b_p_ro = TRUE;
5064 break;
5065 case 2:
5066 break;
5067 case 3:
5068 swap_exists_action = SEA_RECOVER;
5069 break;
5070 case 4:
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005071 mch_remove(fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005072 break;
5073 case 5:
5074 swap_exists_action = SEA_QUIT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005075 break;
5076 case 6:
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005077 swap_exists_action = SEA_QUIT;
5078 got_int = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005079 break;
5080 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005081
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005082 // If the file was deleted this fname can be used.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005083 if (mch_getperm(fname) < 0)
5084 break;
5085 }
5086 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005087 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01005088 msg_puts("\n");
Bram Moolenaar4770d092006-01-12 23:22:24 +00005089 if (msg_silent == 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005090 // call wait_return() later
Bram Moolenaar4770d092006-01-12 23:22:24 +00005091 need_wait_return = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005092 }
5093
5094#ifdef CREATE_DUMMY_FILE
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005095 // Going to try another name, need the dummy file again.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005096 if (did_use_dummy)
Bram Moolenaar69c35002013-11-04 02:54:12 +01005097 dummyfd = mch_fopen((char *)buf_fname, "w");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005098#endif
5099 }
5100 }
5101 }
5102
5103 /*
5104 * Change the ".swp" extension to find another file that can be used.
5105 * First decrement the last char: ".swo", ".swn", etc.
5106 * If that still isn't enough decrement the last but one char: ".svz"
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00005107 * Can happen when editing many "No Name" buffers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005108 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005109 if (fname[n - 1] == 'a') // ".s?a"
Bram Moolenaar071d4272004-06-13 20:20:40 +00005110 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005111 if (fname[n - 2] == 'a') // ".saa": tried enough, give up
Bram Moolenaar071d4272004-06-13 20:20:40 +00005112 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005113 emsg(_("E326: Too many swap files found"));
Bram Moolenaard23a8232018-02-10 18:45:26 +01005114 VIM_CLEAR(fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005115 break;
5116 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005117 --fname[n - 2]; // ".svz", ".suz", etc.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005118 fname[n - 1] = 'z' + 1;
5119 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005120 --fname[n - 1]; // ".swo", ".swn", etc.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005121 }
5122
5123 vim_free(dir_name);
5124#ifdef CREATE_DUMMY_FILE
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005125 if (dummyfd != NULL) // file has been created temporarily
Bram Moolenaar071d4272004-06-13 20:20:40 +00005126 {
5127 fclose(dummyfd);
Bram Moolenaar69c35002013-11-04 02:54:12 +01005128 mch_remove(buf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005129 }
5130#endif
Bram Moolenaar4f974752019-02-17 17:44:42 +01005131#ifdef MSWIN
Bram Moolenaar69c35002013-11-04 02:54:12 +01005132 if (buf_fname != buf->b_fname)
5133 vim_free(buf_fname);
5134#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005135 return fname;
5136}
5137
5138 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005139b0_magic_wrong(ZERO_BL *b0p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005140{
5141 return (b0p->b0_magic_long != (long)B0_MAGIC_LONG
5142 || b0p->b0_magic_int != (int)B0_MAGIC_INT
5143 || b0p->b0_magic_short != (short)B0_MAGIC_SHORT
5144 || b0p->b0_magic_char != B0_MAGIC_CHAR);
5145}
5146
5147#ifdef CHECK_INODE
5148/*
5149 * Compare current file name with file name from swap file.
5150 * Try to use inode numbers when possible.
5151 * Return non-zero when files are different.
5152 *
5153 * When comparing file names a few things have to be taken into consideration:
5154 * - When working over a network the full path of a file depends on the host.
5155 * We check the inode number if possible. It is not 100% reliable though,
5156 * because the device number cannot be used over a network.
5157 * - When a file does not exist yet (editing a new file) there is no inode
5158 * number.
5159 * - The file name in a swap file may not be valid on the current host. The
5160 * "~user" form is used whenever possible to avoid this.
5161 *
5162 * This is getting complicated, let's make a table:
5163 *
5164 * ino_c ino_s fname_c fname_s differ =
5165 *
5166 * both files exist -> compare inode numbers:
5167 * != 0 != 0 X X ino_c != ino_s
5168 *
5169 * inode number(s) unknown, file names available -> compare file names
5170 * == 0 X OK OK fname_c != fname_s
5171 * X == 0 OK OK fname_c != fname_s
5172 *
5173 * current file doesn't exist, file for swap file exist, file name(s) not
5174 * available -> probably different
5175 * == 0 != 0 FAIL X TRUE
5176 * == 0 != 0 X FAIL TRUE
5177 *
5178 * current file exists, inode for swap unknown, file name(s) not
5179 * available -> probably different
5180 * != 0 == 0 FAIL X TRUE
5181 * != 0 == 0 X FAIL TRUE
5182 *
5183 * current file doesn't exist, inode for swap unknown, one file name not
5184 * available -> probably different
5185 * == 0 == 0 FAIL OK TRUE
5186 * == 0 == 0 OK FAIL TRUE
5187 *
5188 * current file doesn't exist, inode for swap unknown, both file names not
Bram Moolenaar8c3169c2018-05-12 17:04:12 +02005189 * available -> compare file names
5190 * == 0 == 0 FAIL FAIL fname_c != fname_s
Bram Moolenaar071d4272004-06-13 20:20:40 +00005191 *
5192 * Note that when the ino_t is 64 bits, only the last 32 will be used. This
5193 * can't be changed without making the block 0 incompatible with 32 bit
5194 * versions.
5195 */
5196
5197 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005198fnamecmp_ino(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005199 char_u *fname_c, // current file name
5200 char_u *fname_s, // file name from swap file
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005201 long ino_block0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005202{
Bram Moolenaar8767f522016-07-01 17:17:39 +02005203 stat_T st;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005204 ino_t ino_c = 0; // ino of current file
5205 ino_t ino_s; // ino of file from swap file
5206 char_u buf_c[MAXPATHL]; // full path of fname_c
5207 char_u buf_s[MAXPATHL]; // full path of fname_s
5208 int retval_c; // flag: buf_c valid
5209 int retval_s; // flag: buf_s valid
Bram Moolenaar071d4272004-06-13 20:20:40 +00005210
5211 if (mch_stat((char *)fname_c, &st) == 0)
5212 ino_c = (ino_t)st.st_ino;
5213
5214 /*
5215 * First we try to get the inode from the file name, because the inode in
5216 * the swap file may be outdated. If that fails (e.g. this path is not
5217 * valid on this machine), use the inode from block 0.
5218 */
5219 if (mch_stat((char *)fname_s, &st) == 0)
5220 ino_s = (ino_t)st.st_ino;
5221 else
5222 ino_s = (ino_t)ino_block0;
5223
5224 if (ino_c && ino_s)
5225 return (ino_c != ino_s);
5226
5227 /*
5228 * One of the inode numbers is unknown, try a forced vim_FullName() and
5229 * compare the file names.
5230 */
5231 retval_c = vim_FullName(fname_c, buf_c, MAXPATHL, TRUE);
5232 retval_s = vim_FullName(fname_s, buf_s, MAXPATHL, TRUE);
5233 if (retval_c == OK && retval_s == OK)
Bram Moolenaar8c3169c2018-05-12 17:04:12 +02005234 return STRCMP(buf_c, buf_s) != 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005235
5236 /*
5237 * Can't compare inodes or file names, guess that the files are different,
Bram Moolenaar8c3169c2018-05-12 17:04:12 +02005238 * unless both appear not to exist at all, then compare with the file name
5239 * in the swap file.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005240 */
5241 if (ino_s == 0 && ino_c == 0 && retval_c == FAIL && retval_s == FAIL)
Bram Moolenaar8c3169c2018-05-12 17:04:12 +02005242 return STRCMP(fname_c, fname_s) != 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005243 return TRUE;
5244}
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005245#endif // CHECK_INODE
Bram Moolenaar071d4272004-06-13 20:20:40 +00005246
5247/*
5248 * Move a long integer into a four byte character array.
5249 * Used for machine independency in block zero.
5250 */
5251 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005252long_to_char(long n, char_u *s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005253{
5254 s[0] = (char_u)(n & 0xff);
5255 n = (unsigned)n >> 8;
5256 s[1] = (char_u)(n & 0xff);
5257 n = (unsigned)n >> 8;
5258 s[2] = (char_u)(n & 0xff);
5259 n = (unsigned)n >> 8;
5260 s[3] = (char_u)(n & 0xff);
5261}
5262
5263 static long
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005264char_to_long(char_u *s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005265{
5266 long retval;
5267
5268 retval = s[3];
5269 retval <<= 8;
5270 retval |= s[2];
5271 retval <<= 8;
5272 retval |= s[1];
5273 retval <<= 8;
5274 retval |= s[0];
5275
5276 return retval;
5277}
5278
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005279/*
5280 * Set the flags in the first block of the swap file:
5281 * - file is modified or not: buf->b_changed
5282 * - 'fileformat'
5283 * - 'fileencoding'
5284 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005285 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005286ml_setflags(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005287{
5288 bhdr_T *hp;
5289 ZERO_BL *b0p;
5290
5291 if (!buf->b_ml.ml_mfp)
5292 return;
5293 for (hp = buf->b_ml.ml_mfp->mf_used_last; hp != NULL; hp = hp->bh_prev)
5294 {
5295 if (hp->bh_bnum == 0)
5296 {
5297 b0p = (ZERO_BL *)(hp->bh_data);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005298 b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0;
5299 b0p->b0_flags = (b0p->b0_flags & ~B0_FF_MASK)
5300 | (get_fileformat(buf) + 1);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005301 add_b0_fenc(b0p, buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005302 hp->bh_flags |= BH_DIRTY;
5303 mf_sync(buf->b_ml.ml_mfp, MFS_ZERO);
5304 break;
5305 }
5306 }
5307}
5308
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005309#if defined(FEAT_CRYPT) || defined(PROTO)
5310/*
5311 * If "data" points to a data block encrypt the text in it and return a copy
5312 * in allocated memory. Return NULL when out of memory.
5313 * Otherwise return "data".
5314 */
5315 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005316ml_encrypt_data(
5317 memfile_T *mfp,
5318 char_u *data,
Bram Moolenaar8767f522016-07-01 17:17:39 +02005319 off_T offset,
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005320 unsigned size)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005321{
5322 DATA_BL *dp = (DATA_BL *)data;
5323 char_u *head_end;
5324 char_u *text_start;
5325 char_u *new_data;
5326 int text_len;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005327 cryptstate_T *state;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005328
5329 if (dp->db_id != DATA_ID)
5330 return data;
5331
Bram Moolenaarbc563362015-06-09 18:35:25 +02005332 state = ml_crypt_prepare(mfp, offset, FALSE);
5333 if (state == NULL)
5334 return data;
5335
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005336 new_data = alloc(size);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005337 if (new_data == NULL)
5338 return NULL;
5339 head_end = (char_u *)(&dp->db_index[dp->db_line_count]);
5340 text_start = (char_u *)dp + dp->db_txt_start;
5341 text_len = size - dp->db_txt_start;
5342
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005343 // Copy the header and the text.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005344 mch_memmove(new_data, dp, head_end - (char_u *)dp);
5345
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005346 // Encrypt the text.
Christian Brabandtf573c6e2021-06-20 14:02:16 +02005347 crypt_encode(state, text_start, text_len, new_data + dp->db_txt_start,
5348 FALSE);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005349 crypt_free_state(state);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005350
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005351 // Clear the gap.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005352 if (head_end < text_start)
5353 vim_memset(new_data + (head_end - data), 0, text_start - head_end);
5354
5355 return new_data;
5356}
5357
5358/*
Bram Moolenaarbc563362015-06-09 18:35:25 +02005359 * Decrypt the text in "data" if it points to an encrypted data block.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005360 */
5361 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005362ml_decrypt_data(
5363 memfile_T *mfp,
5364 char_u *data,
Bram Moolenaar8767f522016-07-01 17:17:39 +02005365 off_T offset,
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005366 unsigned size)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005367{
5368 DATA_BL *dp = (DATA_BL *)data;
5369 char_u *head_end;
5370 char_u *text_start;
5371 int text_len;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005372 cryptstate_T *state;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005373
5374 if (dp->db_id == DATA_ID)
5375 {
5376 head_end = (char_u *)(&dp->db_index[dp->db_line_count]);
5377 text_start = (char_u *)dp + dp->db_txt_start;
5378 text_len = dp->db_txt_end - dp->db_txt_start;
5379
5380 if (head_end > text_start || dp->db_txt_start > size
5381 || dp->db_txt_end > size)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005382 return; // data was messed up
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005383
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005384 state = ml_crypt_prepare(mfp, offset, TRUE);
Bram Moolenaarbc563362015-06-09 18:35:25 +02005385 if (state != NULL)
5386 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005387 // Decrypt the text in place.
Christian Brabandtf573c6e2021-06-20 14:02:16 +02005388 crypt_decode_inplace(state, text_start, text_len, FALSE);
Bram Moolenaarbc563362015-06-09 18:35:25 +02005389 crypt_free_state(state);
5390 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005391 }
5392}
5393
5394/*
5395 * Prepare for encryption/decryption, using the key, seed and offset.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005396 * Return an allocated cryptstate_T *.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005397 */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005398 static cryptstate_T *
Bram Moolenaar8767f522016-07-01 17:17:39 +02005399ml_crypt_prepare(memfile_T *mfp, off_T offset, int reading)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005400{
5401 buf_T *buf = mfp->mf_buffer;
5402 char_u salt[50];
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005403 int method_nr;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005404 char_u *key;
5405 char_u *seed;
5406
5407 if (reading && mfp->mf_old_key != NULL)
5408 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005409 // Reading back blocks with the previous key/method/seed.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005410 method_nr = mfp->mf_old_cm;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005411 key = mfp->mf_old_key;
5412 seed = mfp->mf_old_seed;
5413 }
5414 else
5415 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005416 method_nr = crypt_get_method_nr(buf);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005417 key = buf->b_p_key;
5418 seed = mfp->mf_seed;
5419 }
Bram Moolenaarbc563362015-06-09 18:35:25 +02005420 if (*key == NUL)
5421 return NULL;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005422
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005423 if (method_nr == CRYPT_M_ZIP)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005424 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005425 // For PKzip: Append the offset to the key, so that we use a different
5426 // key for every block.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005427 vim_snprintf((char *)salt, sizeof(salt), "%s%ld", key, (long)offset);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005428 return crypt_create(method_nr, salt, NULL, 0, NULL, 0);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005429 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005430
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005431 // Using blowfish or better: add salt and seed. We use the byte offset
5432 // of the block for the salt.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005433 vim_snprintf((char *)salt, sizeof(salt), "%ld", (long)offset);
5434 return crypt_create(method_nr, key, salt, (int)STRLEN(salt),
Christian Brabandtf573c6e2021-06-20 14:02:16 +02005435 seed, MF_SEED_LEN);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005436}
5437
5438#endif
5439
5440
Bram Moolenaar071d4272004-06-13 20:20:40 +00005441#if defined(FEAT_BYTEOFF) || defined(PROTO)
5442
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005443#define MLCS_MAXL 800 // max no of lines in chunk
5444#define MLCS_MINL 400 // should be half of MLCS_MAXL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005445
5446/*
Bram Moolenaar0ad014c2010-07-25 14:00:46 +02005447 * Keep information for finding byte offset of a line, updtype may be one of:
Bram Moolenaar071d4272004-06-13 20:20:40 +00005448 * ML_CHNK_ADDLINE: Add len to parent chunk, possibly splitting it
5449 * Careful: ML_CHNK_ADDLINE may cause ml_find_line() to be called.
5450 * ML_CHNK_DELLINE: Subtract len from parent chunk, possibly deleting it
5451 * ML_CHNK_UPDLINE: Add len to parent chunk, as a signed entity.
5452 */
5453 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005454ml_updatechunk(
5455 buf_T *buf,
5456 linenr_T line,
5457 long len,
5458 int updtype)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005459{
5460 static buf_T *ml_upd_lastbuf = NULL;
5461 static linenr_T ml_upd_lastline;
5462 static linenr_T ml_upd_lastcurline;
5463 static int ml_upd_lastcurix;
5464
5465 linenr_T curline = ml_upd_lastcurline;
5466 int curix = ml_upd_lastcurix;
5467 long size;
5468 chunksize_T *curchnk;
5469 int rest;
5470 bhdr_T *hp;
5471 DATA_BL *dp;
5472
5473 if (buf->b_ml.ml_usedchunks == -1 || len == 0)
5474 return;
5475 if (buf->b_ml.ml_chunksize == NULL)
5476 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005477 buf->b_ml.ml_chunksize = ALLOC_MULT(chunksize_T, 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005478 if (buf->b_ml.ml_chunksize == NULL)
5479 {
5480 buf->b_ml.ml_usedchunks = -1;
5481 return;
5482 }
5483 buf->b_ml.ml_numchunks = 100;
5484 buf->b_ml.ml_usedchunks = 1;
5485 buf->b_ml.ml_chunksize[0].mlcs_numlines = 1;
5486 buf->b_ml.ml_chunksize[0].mlcs_totalsize = 1;
5487 }
5488
5489 if (updtype == ML_CHNK_UPDLINE && buf->b_ml.ml_line_count == 1)
5490 {
5491 /*
5492 * First line in empty buffer from ml_flush_line() -- reset
5493 */
5494 buf->b_ml.ml_usedchunks = 1;
5495 buf->b_ml.ml_chunksize[0].mlcs_numlines = 1;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01005496 buf->b_ml.ml_chunksize[0].mlcs_totalsize = (long)buf->b_ml.ml_line_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005497 return;
5498 }
5499
5500 /*
5501 * Find chunk that our line belongs to, curline will be at start of the
5502 * chunk.
5503 */
5504 if (buf != ml_upd_lastbuf || line != ml_upd_lastline + 1
5505 || updtype != ML_CHNK_ADDLINE)
5506 {
5507 for (curline = 1, curix = 0;
5508 curix < buf->b_ml.ml_usedchunks - 1
5509 && line >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines;
5510 curix++)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005511 curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005512 }
Bram Moolenaara9a8e042018-10-30 22:15:55 +01005513 else if (curix < buf->b_ml.ml_usedchunks - 1
5514 && line >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005515 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005516 // Adjust cached curix & curline
Bram Moolenaar071d4272004-06-13 20:20:40 +00005517 curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
5518 curix++;
5519 }
5520 curchnk = buf->b_ml.ml_chunksize + curix;
5521
5522 if (updtype == ML_CHNK_DELLINE)
Bram Moolenaar5a6404c2006-11-01 17:12:57 +00005523 len = -len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005524 curchnk->mlcs_totalsize += len;
5525 if (updtype == ML_CHNK_ADDLINE)
5526 {
5527 curchnk->mlcs_numlines++;
5528
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005529 // May resize here so we don't have to do it in both cases below
Bram Moolenaar071d4272004-06-13 20:20:40 +00005530 if (buf->b_ml.ml_usedchunks + 1 >= buf->b_ml.ml_numchunks)
5531 {
Bram Moolenaar9abd5c62015-02-10 18:34:01 +01005532 chunksize_T *t_chunksize = buf->b_ml.ml_chunksize;
5533
Bram Moolenaar071d4272004-06-13 20:20:40 +00005534 buf->b_ml.ml_numchunks = buf->b_ml.ml_numchunks * 3 / 2;
Bram Moolenaar51b6eb42020-08-22 15:19:18 +02005535 buf->b_ml.ml_chunksize = vim_realloc(buf->b_ml.ml_chunksize,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005536 sizeof(chunksize_T) * buf->b_ml.ml_numchunks);
5537 if (buf->b_ml.ml_chunksize == NULL)
5538 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005539 // Hmmmm, Give up on offset for this buffer
Bram Moolenaar9abd5c62015-02-10 18:34:01 +01005540 vim_free(t_chunksize);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005541 buf->b_ml.ml_usedchunks = -1;
5542 return;
5543 }
5544 }
5545
5546 if (buf->b_ml.ml_chunksize[curix].mlcs_numlines >= MLCS_MAXL)
5547 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005548 int count; // number of entries in block
Bram Moolenaar071d4272004-06-13 20:20:40 +00005549 int idx;
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005550 int end_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005551 int text_end;
5552 int linecnt;
5553
5554 mch_memmove(buf->b_ml.ml_chunksize + curix + 1,
5555 buf->b_ml.ml_chunksize + curix,
5556 (buf->b_ml.ml_usedchunks - curix) *
5557 sizeof(chunksize_T));
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005558 // Compute length of first half of lines in the split chunk
Bram Moolenaar071d4272004-06-13 20:20:40 +00005559 size = 0;
5560 linecnt = 0;
5561 while (curline < buf->b_ml.ml_line_count
5562 && linecnt < MLCS_MINL)
5563 {
5564 if ((hp = ml_find_line(buf, curline, ML_FIND)) == NULL)
5565 {
5566 buf->b_ml.ml_usedchunks = -1;
5567 return;
5568 }
5569 dp = (DATA_BL *)(hp->bh_data);
5570 count = (long)(buf->b_ml.ml_locked_high) -
5571 (long)(buf->b_ml.ml_locked_low) + 1;
5572 idx = curline - buf->b_ml.ml_locked_low;
5573 curline = buf->b_ml.ml_locked_high + 1;
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005574
5575 // compute index of last line to use in this MEMLINE
Bram Moolenaar071d4272004-06-13 20:20:40 +00005576 rest = count - idx;
5577 if (linecnt + rest > MLCS_MINL)
5578 {
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005579 end_idx = idx + MLCS_MINL - linecnt - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005580 linecnt = MLCS_MINL;
5581 }
5582 else
5583 {
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005584 end_idx = count - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005585 linecnt += rest;
5586 }
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01005587#ifdef FEAT_PROP_POPUP
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005588 if (buf->b_has_textprop)
5589 {
5590 int i;
5591
5592 // We cannot use the text pointers to get the text length,
5593 // the text prop info would also be counted. Go over the
5594 // lines.
5595 for (i = end_idx; i < idx; ++i)
Bram Moolenaar4b7214e2019-01-03 21:55:32 +01005596 size += (int)STRLEN((char_u *)dp + (dp->db_index[i] & DB_INDEX_MASK)) + 1;
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005597 }
5598 else
5599#endif
5600 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005601 if (idx == 0)// first line in block, text at the end
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005602 text_end = dp->db_txt_end;
5603 else
5604 text_end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK);
5605 size += text_end - ((dp->db_index[end_idx]) & DB_INDEX_MASK);
5606 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005607 }
5608 buf->b_ml.ml_chunksize[curix].mlcs_numlines = linecnt;
5609 buf->b_ml.ml_chunksize[curix + 1].mlcs_numlines -= linecnt;
5610 buf->b_ml.ml_chunksize[curix].mlcs_totalsize = size;
5611 buf->b_ml.ml_chunksize[curix + 1].mlcs_totalsize -= size;
5612 buf->b_ml.ml_usedchunks++;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005613 ml_upd_lastbuf = NULL; // Force recalc of curix & curline
Bram Moolenaar071d4272004-06-13 20:20:40 +00005614 return;
5615 }
5616 else if (buf->b_ml.ml_chunksize[curix].mlcs_numlines >= MLCS_MINL
5617 && curix == buf->b_ml.ml_usedchunks - 1
5618 && buf->b_ml.ml_line_count - line <= 1)
5619 {
5620 /*
Bram Moolenaar8e7d6222020-12-18 19:49:56 +01005621 * We are in the last chunk and it is cheap to create a new one
Bram Moolenaar071d4272004-06-13 20:20:40 +00005622 * after this. Do it now to avoid the loop above later on
5623 */
5624 curchnk = buf->b_ml.ml_chunksize + curix + 1;
5625 buf->b_ml.ml_usedchunks++;
5626 if (line == buf->b_ml.ml_line_count)
5627 {
5628 curchnk->mlcs_numlines = 0;
5629 curchnk->mlcs_totalsize = 0;
5630 }
5631 else
5632 {
5633 /*
5634 * Line is just prior to last, move count for last
5635 * This is the common case when loading a new file
5636 */
5637 hp = ml_find_line(buf, buf->b_ml.ml_line_count, ML_FIND);
5638 if (hp == NULL)
5639 {
5640 buf->b_ml.ml_usedchunks = -1;
5641 return;
5642 }
5643 dp = (DATA_BL *)(hp->bh_data);
5644 if (dp->db_line_count == 1)
5645 rest = dp->db_txt_end - dp->db_txt_start;
5646 else
5647 rest =
5648 ((dp->db_index[dp->db_line_count - 2]) & DB_INDEX_MASK)
5649 - dp->db_txt_start;
5650 curchnk->mlcs_totalsize = rest;
5651 curchnk->mlcs_numlines = 1;
5652 curchnk[-1].mlcs_totalsize -= rest;
5653 curchnk[-1].mlcs_numlines -= 1;
5654 }
5655 }
5656 }
5657 else if (updtype == ML_CHNK_DELLINE)
5658 {
5659 curchnk->mlcs_numlines--;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005660 ml_upd_lastbuf = NULL; // Force recalc of curix & curline
Bram Moolenaar071d4272004-06-13 20:20:40 +00005661 if (curix < (buf->b_ml.ml_usedchunks - 1)
5662 && (curchnk->mlcs_numlines + curchnk[1].mlcs_numlines)
5663 <= MLCS_MINL)
5664 {
5665 curix++;
5666 curchnk = buf->b_ml.ml_chunksize + curix;
5667 }
5668 else if (curix == 0 && curchnk->mlcs_numlines <= 0)
5669 {
5670 buf->b_ml.ml_usedchunks--;
5671 mch_memmove(buf->b_ml.ml_chunksize, buf->b_ml.ml_chunksize + 1,
5672 buf->b_ml.ml_usedchunks * sizeof(chunksize_T));
5673 return;
5674 }
5675 else if (curix == 0 || (curchnk->mlcs_numlines > 10
5676 && (curchnk->mlcs_numlines + curchnk[-1].mlcs_numlines)
5677 > MLCS_MINL))
5678 {
5679 return;
5680 }
5681
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005682 // Collapse chunks
Bram Moolenaar071d4272004-06-13 20:20:40 +00005683 curchnk[-1].mlcs_numlines += curchnk->mlcs_numlines;
5684 curchnk[-1].mlcs_totalsize += curchnk->mlcs_totalsize;
5685 buf->b_ml.ml_usedchunks--;
5686 if (curix < buf->b_ml.ml_usedchunks)
5687 {
5688 mch_memmove(buf->b_ml.ml_chunksize + curix,
5689 buf->b_ml.ml_chunksize + curix + 1,
5690 (buf->b_ml.ml_usedchunks - curix) *
5691 sizeof(chunksize_T));
5692 }
5693 return;
5694 }
5695 ml_upd_lastbuf = buf;
5696 ml_upd_lastline = line;
5697 ml_upd_lastcurline = curline;
5698 ml_upd_lastcurix = curix;
5699}
5700
5701/*
5702 * Find offset for line or line with offset.
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005703 * Find line with offset if "lnum" is 0; return remaining offset in offp
5704 * Find offset of line if "lnum" > 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00005705 * return -1 if information is not available
5706 */
5707 long
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005708ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005709{
5710 linenr_T curline;
5711 int curix;
5712 long size;
5713 bhdr_T *hp;
5714 DATA_BL *dp;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005715 int count; // number of entries in block
Bram Moolenaar071d4272004-06-13 20:20:40 +00005716 int idx;
5717 int start_idx;
5718 int text_end;
5719 long offset;
5720 int len;
5721 int ffdos = (get_fileformat(buf) == EOL_DOS);
5722 int extra = 0;
5723
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005724 // take care of cached line first
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005725 ml_flush_line(curbuf);
5726
Bram Moolenaar071d4272004-06-13 20:20:40 +00005727 if (buf->b_ml.ml_usedchunks == -1
5728 || buf->b_ml.ml_chunksize == NULL
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005729 || lnum < 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005730 return -1;
5731
5732 if (offp == NULL)
5733 offset = 0;
5734 else
5735 offset = *offp;
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005736 if (lnum == 0 && offset <= 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005737 return 1; // Not a "find offset" and offset 0 _must_ be in line 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00005738 /*
5739 * Find the last chunk before the one containing our line. Last chunk is
5740 * special because it will never qualify
5741 */
5742 curline = 1;
5743 curix = size = 0;
5744 while (curix < buf->b_ml.ml_usedchunks - 1
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005745 && ((lnum != 0
5746 && lnum >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005747 || (offset != 0
5748 && offset > size + buf->b_ml.ml_chunksize[curix].mlcs_totalsize
5749 + ffdos * buf->b_ml.ml_chunksize[curix].mlcs_numlines)))
5750 {
5751 curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
5752 size += buf->b_ml.ml_chunksize[curix].mlcs_totalsize;
5753 if (offset && ffdos)
5754 size += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
5755 curix++;
5756 }
5757
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005758 while ((lnum != 0 && curline < lnum) || (offset != 0 && size < offset))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005759 {
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005760#ifdef FEAT_PROP_POPUP
5761 size_t textprop_total = 0;
5762#endif
5763
Bram Moolenaar071d4272004-06-13 20:20:40 +00005764 if (curline > buf->b_ml.ml_line_count
5765 || (hp = ml_find_line(buf, curline, ML_FIND)) == NULL)
5766 return -1;
5767 dp = (DATA_BL *)(hp->bh_data);
5768 count = (long)(buf->b_ml.ml_locked_high) -
5769 (long)(buf->b_ml.ml_locked_low) + 1;
5770 start_idx = idx = curline - buf->b_ml.ml_locked_low;
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005771 if (idx == 0) // first line in block, text at the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00005772 text_end = dp->db_txt_end;
5773 else
5774 text_end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005775 // Compute index of last line to use in this MEMLINE
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005776 if (lnum != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005777 {
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005778 if (curline + (count - idx) >= lnum)
5779 idx += lnum - curline - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005780 else
5781 idx = count - 1;
5782 }
5783 else
5784 {
5785 extra = 0;
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005786 for (;;)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005787 {
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005788#ifdef FEAT_PROP_POPUP
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005789 size_t textprop_size = 0;
5790
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005791 if (buf->b_has_textprop)
5792 {
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005793 char_u *l1, *l2;
5794
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005795 // compensate for the extra bytes taken by textprops
5796 l1 = (char_u *)dp + ((dp->db_index[idx]) & DB_INDEX_MASK);
5797 l2 = (char_u *)dp + (idx == 0 ? dp->db_txt_end
5798 : ((dp->db_index[idx - 1]) & DB_INDEX_MASK));
5799 textprop_size = (l2 - l1) - (STRLEN(l1) + 1);
5800 }
5801#endif
5802 if (!(offset >= size
5803 + text_end - (int)((dp->db_index[idx]) & DB_INDEX_MASK)
5804#ifdef FEAT_PROP_POPUP
Bram Moolenaar94b6fb72020-01-17 21:00:59 +01005805 - (long)(textprop_total + textprop_size)
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005806#endif
5807 + ffdos))
5808 break;
5809
Bram Moolenaar071d4272004-06-13 20:20:40 +00005810 if (ffdos)
5811 size++;
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005812#ifdef FEAT_PROP_POPUP
5813 textprop_total += textprop_size;
5814#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005815 if (idx == count - 1)
5816 {
5817 extra = 1;
5818 break;
5819 }
5820 idx++;
5821 }
5822 }
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01005823#ifdef FEAT_PROP_POPUP
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005824 if (buf->b_has_textprop && lnum != 0)
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005825 {
5826 int i;
5827
5828 // cannot use the db_index pointer, need to get the actual text
5829 // lengths.
5830 len = 0;
5831 for (i = start_idx; i <= idx; ++i)
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005832 {
5833 char_u *p = (char_u *)dp + ((dp->db_index[i]) & DB_INDEX_MASK);
5834 len += (int)STRLEN(p) + 1;
5835 }
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005836 }
5837 else
5838#endif
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005839 len = text_end - ((dp->db_index[idx]) & DB_INDEX_MASK)
5840#ifdef FEAT_PROP_POPUP
5841 - (long)textprop_total
5842#endif
5843 ;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005844 size += len;
5845 if (offset != 0 && size >= offset)
5846 {
5847 if (size + ffdos == offset)
5848 *offp = 0;
5849 else if (idx == start_idx)
5850 *offp = offset - size + len;
5851 else
5852 *offp = offset - size + len
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005853 - (text_end - ((dp->db_index[idx - 1]) & DB_INDEX_MASK))
5854#ifdef FEAT_PROP_POPUP
5855 + (long)textprop_total
5856#endif
5857 ;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005858 curline += idx - start_idx + extra;
5859 if (curline > buf->b_ml.ml_line_count)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005860 return -1; // exactly one byte beyond the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00005861 return curline;
5862 }
5863 curline = buf->b_ml.ml_locked_high + 1;
5864 }
5865
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005866 if (lnum != 0)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005867 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005868 // Count extra CR characters.
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005869 if (ffdos)
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005870 size += lnum - 1;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005871
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005872 // Don't count the last line break if 'noeol' and ('bin' or
5873 // 'nofixeol').
Bram Moolenaar34d72d42015-07-17 14:18:08 +02005874 if ((!buf->b_p_fixeol || buf->b_p_bin) && !buf->b_p_eol
Bram Moolenaarc26f7c62018-08-20 22:53:04 +02005875 && lnum > buf->b_ml.ml_line_count)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005876 size -= ffdos + 1;
5877 }
5878
Bram Moolenaar071d4272004-06-13 20:20:40 +00005879 return size;
5880}
5881
5882/*
5883 * Goto byte in buffer with offset 'cnt'.
5884 */
5885 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005886goto_byte(long cnt)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005887{
5888 long boff = cnt;
5889 linenr_T lnum;
5890
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005891 ml_flush_line(curbuf); // cached line may be dirty
Bram Moolenaar071d4272004-06-13 20:20:40 +00005892 setpcmark();
5893 if (boff)
5894 --boff;
5895 lnum = ml_find_line_or_offset(curbuf, (linenr_T)0, &boff);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005896 if (lnum < 1) // past the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00005897 {
5898 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
5899 curwin->w_curswant = MAXCOL;
5900 coladvance((colnr_T)MAXCOL);
5901 }
5902 else
5903 {
5904 curwin->w_cursor.lnum = lnum;
5905 curwin->w_cursor.col = (colnr_T)boff;
Bram Moolenaar943d2b52005-12-02 00:50:49 +00005906 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005907 curwin->w_set_curswant = TRUE;
5908 }
5909 check_cursor();
5910
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005911 // Make sure the cursor is on the first byte of a multi-byte char.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005912 if (has_mbyte)
5913 mb_adjust_cursor();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005914}
5915#endif