blob: 527552cff2bb96e9f1313107f36d47527c9d08b7 [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
Bram Moolenaar5a6404c2006-11-01 17:12:57 +000051#if defined(SASC) || defined(__amigaos4__)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010052# include <proto/dos.h> // for Open() and Close()
Bram Moolenaar071d4272004-06-13 20:20:40 +000053#endif
54
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010055typedef struct block0 ZERO_BL; // contents of the first block
56typedef struct pointer_block PTR_BL; // contents of a pointer block
57typedef struct data_block DATA_BL; // contents of a data block
58typedef struct pointer_entry PTR_EN; // block/line-count pair
Bram Moolenaar071d4272004-06-13 20:20:40 +000059
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010060#define DATA_ID (('d' << 8) + 'a') // data block id
61#define PTR_ID (('p' << 8) + 't') // pointer block id
62#define BLOCK0_ID0 'b' // block 0 id 0
63#define BLOCK0_ID1 '0' // block 0 id 1
64#define BLOCK0_ID1_C0 'c' // block 0 id 1 'cm' 0
65#define BLOCK0_ID1_C1 'C' // block 0 id 1 'cm' 1
66#define BLOCK0_ID1_C2 'd' // block 0 id 1 'cm' 2
Bram Moolenaare1b48222023-04-24 18:11:35 +010067// BLOCK0_ID1_C3 and BLOCK0_ID1_C4 are for libsodium enctyption. However, for
68// these the swapfile is disabled, thus they will not be used. Added for
69// consistency anyway.
70#define BLOCK0_ID1_C3 'S' // block 0 id 1 'cm' 3
71#define BLOCK0_ID1_C4 's' // block 0 id 1 'cm' 4
Bram Moolenaar8f4ac012014-08-10 13:38:34 +020072
73#if defined(FEAT_CRYPT)
74static int id1_codes[] = {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010075 BLOCK0_ID1_C0, // CRYPT_M_ZIP
76 BLOCK0_ID1_C1, // CRYPT_M_BF
77 BLOCK0_ID1_C2, // CRYPT_M_BF2
Christian Brabandtf573c6e2021-06-20 14:02:16 +020078 BLOCK0_ID1_C3, // CRYPT_M_SOD - Unused!
Bram Moolenaare1b48222023-04-24 18:11:35 +010079 BLOCK0_ID1_C4, // CRYPT_M_SOD2 - 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
Bram Moolenaarb67ba032023-04-22 21:14:26 +0100106// Value for pb_count_max.
107#define PB_COUNT_MAX(mfp) (short_u)(((mfp)->mf_page_size - offsetof(PTR_BL, pb_pointer)) / sizeof(PTR_EN))
108
Bram Moolenaar071d4272004-06-13 20:20:40 +0000109/*
110 * A data block is a leaf in the tree.
111 *
112 * The text of the lines is at the end of the block. The text of the first line
113 * in the block is put at the end, the text of the second line in front of it,
114 * etc. Thus the order of the lines is the opposite of the line number.
115 */
116struct data_block
117{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100118 short_u db_id; // ID for data block: DATA_ID
119 unsigned db_free; // free space available
120 unsigned db_txt_start; // byte where text starts
121 unsigned db_txt_end; // byte just after data block
122 linenr_T db_line_count; // number of lines in this block
123 unsigned db_index[1]; // index for start of line (actually bigger)
Bram Moolenaar4b96df52020-01-26 22:00:26 +0100124 // followed by empty space up to db_txt_start
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100125 // followed by the text in the lines until
126 // end of page
Bram Moolenaar071d4272004-06-13 20:20:40 +0000127};
128
129/*
130 * The low bits of db_index hold the actual index. The topmost bit is
131 * used for the global command to be able to mark a line.
132 * This method is not clean, but otherwise there would be at least one extra
133 * byte used for each line.
134 * The mark has to be in this place to keep it with the correct line when other
135 * lines are inserted or deleted.
136 */
137#define DB_MARKED ((unsigned)1 << ((sizeof(unsigned) * 8) - 1))
138#define DB_INDEX_MASK (~DB_MARKED)
139
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100140#define INDEX_SIZE (sizeof(unsigned)) // size of one db_index entry
zeertzjq1b438a82023-02-01 13:11:15 +0000141#define HEADER_SIZE (offsetof(DATA_BL, db_index)) // size of data block header
Bram Moolenaar071d4272004-06-13 20:20:40 +0000142
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100143#define B0_FNAME_SIZE_ORG 900 // what it was in older versions
144#define B0_FNAME_SIZE_NOCRYPT 898 // 2 bytes used for other things
145#define B0_FNAME_SIZE_CRYPT 890 // 10 bytes used for other things
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000146#define B0_UNAME_SIZE 40
147#define B0_HNAME_SIZE 40
Bram Moolenaar071d4272004-06-13 20:20:40 +0000148/*
149 * Restrict the numbers to 32 bits, otherwise most compilers will complain.
150 * This won't detect a 64 bit machine that only swaps a byte in the top 32
151 * bits, but that is crazy anyway.
152 */
153#define B0_MAGIC_LONG 0x30313233L
154#define B0_MAGIC_INT 0x20212223L
155#define B0_MAGIC_SHORT 0x10111213L
156#define B0_MAGIC_CHAR 0x55
157
158/*
159 * Block zero holds all info about the swap file.
160 *
161 * NOTE: DEFINITION OF BLOCK 0 SHOULD NOT CHANGE! It would make all existing
162 * swap files unusable!
163 *
164 * If size of block0 changes anyway, adjust MIN_SWAP_PAGE_SIZE in vim.h!!
165 *
Bram Moolenaarbae0c162007-05-10 19:30:25 +0000166 * This block is built up of single bytes, to make it portable across
Bram Moolenaar071d4272004-06-13 20:20:40 +0000167 * different machines. b0_magic_* is used to check the byte order and size of
168 * variables, because the rest of the swap file is not portable.
169 */
170struct block0
171{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100172 char_u b0_id[2]; // id for block 0: BLOCK0_ID0 and BLOCK0_ID1,
173 // BLOCK0_ID1_C0, BLOCK0_ID1_C1, etc.
174 char_u b0_version[10]; // Vim version string
175 char_u b0_page_size[4];// number of bytes per page
176 char_u b0_mtime[4]; // last modification time of file
177 char_u b0_ino[4]; // inode of b0_fname
178 char_u b0_pid[4]; // process id of creator (or 0)
179 char_u b0_uname[B0_UNAME_SIZE]; // name of user (uid if no name)
180 char_u b0_hname[B0_HNAME_SIZE]; // host name (if it has a name)
181 char_u b0_fname[B0_FNAME_SIZE_ORG]; // name of file being edited
182 long b0_magic_long; // check for byte order of long
183 int b0_magic_int; // check for byte order of int
184 short b0_magic_short; // check for byte order of short
185 char_u b0_magic_char; // check for last char
Bram Moolenaar071d4272004-06-13 20:20:40 +0000186};
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000187
188/*
Bram Moolenaar4770d092006-01-12 23:22:24 +0000189 * Note: b0_dirty and b0_flags are put at the end of the file name. For very
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000190 * long file names in older versions of Vim they are invalid.
191 * The 'fileencoding' comes before b0_flags, with a NUL in front. But only
192 * when there is room, for very long file names it's omitted.
193 */
194#define B0_DIRTY 0x55
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200195#define b0_dirty b0_fname[B0_FNAME_SIZE_ORG - 1]
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000196
197/*
198 * The b0_flags field is new in Vim 7.0.
199 */
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200200#define b0_flags b0_fname[B0_FNAME_SIZE_ORG - 2]
201
202/*
203 * Crypt seed goes here, 8 bytes. New in Vim 7.3.
204 * Without encryption these bytes may be used for 'fenc'.
205 */
206#define b0_seed b0_fname[B0_FNAME_SIZE_ORG - 2 - MF_SEED_LEN]
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000207
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100208// The lowest two bits contain the fileformat. Zero means it's not set
209// (compatible with Vim 6.x), otherwise it's EOL_UNIX + 1, EOL_DOS + 1 or
210// EOL_MAC + 1.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000211#define B0_FF_MASK 3
212
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100213// Swap file is in directory of edited file. Used to find the file from
214// different mount points.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000215#define B0_SAME_DIR 4
216
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100217// The 'fileencoding' is at the end of b0_fname[], with a NUL in front of it.
218// When empty there is only the NUL.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000219#define B0_HAS_FENC 8
Bram Moolenaar071d4272004-06-13 20:20:40 +0000220
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100221#define STACK_INCR 5 // nr of entries added to ml_stack at a time
Bram Moolenaar071d4272004-06-13 20:20:40 +0000222
223/*
224 * The line number where the first mark may be is remembered.
225 * If it is 0 there are no marks at all.
226 * (always used for the current buffer only, no buffer change possible while
227 * executing a global command).
228 */
229static linenr_T lowest_marked = 0;
230
231/*
232 * arguments for ml_find_line()
233 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100234#define ML_DELETE 0x11 // delete line
235#define ML_INSERT 0x12 // insert line
236#define ML_FIND 0x13 // just find the line
237#define ML_FLUSH 0x02 // flush locked block
kylo252ae6f1d82022-02-16 19:24:07 +0000238#define ML_SIMPLE(x) ((x) & 0x10) // DEL, INS or FIND
Bram Moolenaar071d4272004-06-13 20:20:40 +0000239
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100240// argument for ml_upd_block0()
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200241typedef enum {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100242 UB_FNAME = 0 // update timestamp and filename
243 , UB_SAME_DIR // update the B0_SAME_DIR flag
244 , UB_CRYPT // update crypt key
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200245} upd_block0_T;
246
247#ifdef FEAT_CRYPT
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100248static void ml_set_b0_crypt(buf_T *buf, ZERO_BL *b0p);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200249#endif
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100250static void ml_upd_block0(buf_T *buf, upd_block0_T what);
251static void set_b0_fname(ZERO_BL *, buf_T *buf);
252static void set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100253static void add_b0_fenc(ZERO_BL *b0p, buf_T *buf);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100254static time_t swapfile_info(char_u *);
255static int recov_file_names(char_u **, char_u *, int prepend_dot);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100256static char_u *findswapname(buf_T *, char_u **, char_u *);
257static void ml_flush_line(buf_T *);
258static bhdr_T *ml_new_data(memfile_T *, int, int);
259static bhdr_T *ml_new_ptr(memfile_T *);
260static bhdr_T *ml_find_line(buf_T *, linenr_T, int);
261static int ml_add_stack(buf_T *);
262static void ml_lineadd(buf_T *, int);
263static int b0_magic_wrong(ZERO_BL *);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000264#ifdef CHECK_INODE
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100265static int fnamecmp_ino(char_u *, char_u *, long);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000266#endif
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100267static void long_to_char(long, char_u *);
268static long char_to_long(char_u *);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200269#ifdef FEAT_CRYPT
Bram Moolenaar8767f522016-07-01 17:17:39 +0200270static cryptstate_T *ml_crypt_prepare(memfile_T *mfp, off_T offset, int reading);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200271#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000272#ifdef FEAT_BYTEOFF
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100273static void ml_updatechunk(buf_T *buf, long line, long len, int updtype);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000274#endif
275
276/*
Bram Moolenaar4770d092006-01-12 23:22:24 +0000277 * Open a new memline for "buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +0000278 *
Bram Moolenaar4770d092006-01-12 23:22:24 +0000279 * Return FAIL for failure, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000280 */
281 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100282ml_open(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000283{
284 memfile_T *mfp;
285 bhdr_T *hp = NULL;
286 ZERO_BL *b0p;
287 PTR_BL *pp;
288 DATA_BL *dp;
289
Bram Moolenaar4770d092006-01-12 23:22:24 +0000290 /*
291 * init fields in memline struct
292 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100293 buf->b_ml.ml_stack_size = 0; // no stack yet
294 buf->b_ml.ml_stack = NULL; // no stack yet
295 buf->b_ml.ml_stack_top = 0; // nothing in the stack
296 buf->b_ml.ml_locked = NULL; // no cached block
297 buf->b_ml.ml_line_lnum = 0; // no cached line
Bram Moolenaar071d4272004-06-13 20:20:40 +0000298#ifdef FEAT_BYTEOFF
Bram Moolenaar4770d092006-01-12 23:22:24 +0000299 buf->b_ml.ml_chunksize = NULL;
Bram Moolenaardf9070e2021-08-25 17:31:37 +0200300 buf->b_ml.ml_usedchunks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000301#endif
302
Bram Moolenaare1004402020-10-24 20:49:43 +0200303 if (cmdmod.cmod_flags & CMOD_NOSWAPFILE)
Bram Moolenaar5803ae62014-03-23 16:04:02 +0100304 buf->b_p_swf = FALSE;
305
Bram Moolenaar4770d092006-01-12 23:22:24 +0000306 /*
307 * When 'updatecount' is non-zero swap file may be opened later.
308 */
309 if (p_uc && buf->b_p_swf)
310 buf->b_may_swap = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000311 else
Bram Moolenaar4770d092006-01-12 23:22:24 +0000312 buf->b_may_swap = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000313
Bram Moolenaar4770d092006-01-12 23:22:24 +0000314 /*
315 * Open the memfile. No swap file is created yet.
316 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000317 mfp = mf_open(NULL, 0);
318 if (mfp == NULL)
319 goto error;
320
Bram Moolenaar4770d092006-01-12 23:22:24 +0000321 buf->b_ml.ml_mfp = mfp;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200322#ifdef FEAT_CRYPT
323 mfp->mf_buffer = buf;
324#endif
Bram Moolenaar4770d092006-01-12 23:22:24 +0000325 buf->b_ml.ml_flags = ML_EMPTY;
326 buf->b_ml.ml_line_count = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000327
Bram Moolenaar071d4272004-06-13 20:20:40 +0000328/*
329 * fill block0 struct and write page 0
330 */
331 if ((hp = mf_new(mfp, FALSE, 1)) == NULL)
332 goto error;
333 if (hp->bh_bnum != 0)
334 {
Bram Moolenaar9a846fb2022-01-01 21:59:18 +0000335 iemsg(_(e_didnt_get_block_nr_zero));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000336 goto error;
337 }
338 b0p = (ZERO_BL *)(hp->bh_data);
339
340 b0p->b0_id[0] = BLOCK0_ID0;
341 b0p->b0_id[1] = BLOCK0_ID1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000342 b0p->b0_magic_long = (long)B0_MAGIC_LONG;
343 b0p->b0_magic_int = (int)B0_MAGIC_INT;
344 b0p->b0_magic_short = (short)B0_MAGIC_SHORT;
345 b0p->b0_magic_char = B0_MAGIC_CHAR;
Bram Moolenaar22c10562018-05-26 17:35:27 +0200346 mch_memmove(b0p->b0_version, "VIM ", 4);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000347 STRNCPY(b0p->b0_version + 4, Version, 6);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000348 long_to_char((long)mfp->mf_page_size, b0p->b0_page_size);
Bram Moolenaar4770d092006-01-12 23:22:24 +0000349
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000350#ifdef FEAT_SPELL
351 if (!buf->b_spell)
352#endif
Bram Moolenaar4770d092006-01-12 23:22:24 +0000353 {
354 b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0;
355 b0p->b0_flags = get_fileformat(buf) + 1;
356 set_b0_fname(b0p, buf);
357 (void)get_user_name(b0p->b0_uname, B0_UNAME_SIZE);
358 b0p->b0_uname[B0_UNAME_SIZE - 1] = NUL;
359 mch_get_host_name(b0p->b0_hname, B0_HNAME_SIZE);
360 b0p->b0_hname[B0_HNAME_SIZE - 1] = NUL;
361 long_to_char(mch_get_pid(), b0p->b0_pid);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200362#ifdef FEAT_CRYPT
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200363 ml_set_b0_crypt(buf, b0p);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200364#endif
Bram Moolenaar4770d092006-01-12 23:22:24 +0000365 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000366
367 /*
368 * Always sync block number 0 to disk, so we can check the file name in
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200369 * the swap file in findswapname(). Don't do this for a help files or
370 * a spell buffer though.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000371 * Only works when there's a swapfile, otherwise it's done when the file
372 * is created.
373 */
374 mf_put(mfp, hp, TRUE, FALSE);
Bram Moolenaar4770d092006-01-12 23:22:24 +0000375 if (!buf->b_help && !B_SPELL(buf))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000376 (void)mf_sync(mfp, 0);
377
Bram Moolenaar4770d092006-01-12 23:22:24 +0000378 /*
379 * Fill in root pointer block and write page 1.
380 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000381 if ((hp = ml_new_ptr(mfp)) == NULL)
382 goto error;
383 if (hp->bh_bnum != 1)
384 {
Bram Moolenaar9a846fb2022-01-01 21:59:18 +0000385 iemsg(_(e_didnt_get_block_nr_one));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000386 goto error;
387 }
388 pp = (PTR_BL *)(hp->bh_data);
389 pp->pb_count = 1;
390 pp->pb_pointer[0].pe_bnum = 2;
391 pp->pb_pointer[0].pe_page_count = 1;
392 pp->pb_pointer[0].pe_old_lnum = 1;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100393 pp->pb_pointer[0].pe_line_count = 1; // line count after insertion
Bram Moolenaar071d4272004-06-13 20:20:40 +0000394 mf_put(mfp, hp, TRUE, FALSE);
395
Bram Moolenaar4770d092006-01-12 23:22:24 +0000396 /*
397 * Allocate first data block and create an empty line 1.
398 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000399 if ((hp = ml_new_data(mfp, FALSE, 1)) == NULL)
400 goto error;
401 if (hp->bh_bnum != 2)
402 {
Bram Moolenaar9a846fb2022-01-01 21:59:18 +0000403 iemsg(_(e_didnt_get_block_nr_two));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000404 goto error;
405 }
406
407 dp = (DATA_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100408 dp->db_index[0] = --dp->db_txt_start; // at end of block
Bram Moolenaar071d4272004-06-13 20:20:40 +0000409 dp->db_free -= 1 + INDEX_SIZE;
410 dp->db_line_count = 1;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100411 *((char_u *)dp + dp->db_txt_start) = NUL; // empty line
Bram Moolenaar071d4272004-06-13 20:20:40 +0000412
413 return OK;
414
415error:
416 if (mfp != NULL)
417 {
418 if (hp)
419 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100420 mf_close(mfp, TRUE); // will also free(mfp->mf_fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000421 }
Bram Moolenaar4770d092006-01-12 23:22:24 +0000422 buf->b_ml.ml_mfp = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000423 return FAIL;
424}
425
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200426#if defined(FEAT_CRYPT) || defined(PROTO)
427/*
Bram Moolenaar2be79502014-08-13 21:58:28 +0200428 * Prepare encryption for "buf" for the current key and method.
429 */
430 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100431ml_set_mfp_crypt(buf_T *buf)
Bram Moolenaar2be79502014-08-13 21:58:28 +0200432{
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000433 if (*buf->b_p_key == NUL)
434 return;
Bram Moolenaar2be79502014-08-13 21:58:28 +0200435
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000436 int method_nr = crypt_get_method_nr(buf);
437
438 if (method_nr > CRYPT_M_ZIP && method_nr < CRYPT_M_SOD)
439 {
440 // Generate a seed and store it in the memfile.
441 sha2_seed(buf->b_ml.ml_mfp->mf_seed, MF_SEED_LEN, NULL, 0);
Bram Moolenaar2be79502014-08-13 21:58:28 +0200442 }
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000443#ifdef FEAT_SODIUM
Christian Brabandtaae58342023-04-23 17:50:22 +0100444 else if (crypt_method_is_sodium(method_nr))
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000445 crypt_sodium_randombytes_buf(buf->b_ml.ml_mfp->mf_seed,
446 MF_SEED_LEN);
447#endif
Bram Moolenaar2be79502014-08-13 21:58:28 +0200448}
449
450/*
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200451 * Prepare encryption for "buf" with block 0 "b0p".
Bram Moolenaare1b48222023-04-24 18:11:35 +0100452 * Note: should not be called with libsodium encryption, since xchacha20 does
453 * not support swapfile encryption.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200454 */
455 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100456ml_set_b0_crypt(buf_T *buf, ZERO_BL *b0p)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200457{
458 if (*buf->b_p_key == NUL)
459 b0p->b0_id[1] = BLOCK0_ID1;
460 else
461 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200462 int method_nr = crypt_get_method_nr(buf);
463
464 b0p->b0_id[1] = id1_codes[method_nr];
Christian Brabandtf573c6e2021-06-20 14:02:16 +0200465 if (method_nr > CRYPT_M_ZIP && method_nr < CRYPT_M_SOD)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200466 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100467 // Generate a seed and store it in block 0 and in the memfile.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200468 sha2_seed(&b0p->b0_seed, MF_SEED_LEN, NULL, 0);
469 mch_memmove(buf->b_ml.ml_mfp->mf_seed, &b0p->b0_seed, MF_SEED_LEN);
470 }
471 }
472}
473
474/*
475 * Called after the crypt key or 'cryptmethod' was changed for "buf".
476 * Will apply this to the swapfile.
477 * "old_key" is the previous key. It is equal to buf->b_p_key when
478 * 'cryptmethod' is changed.
Bram Moolenaar49771f42010-07-20 17:32:38 +0200479 * "old_cm" is the previous 'cryptmethod'. It is equal to the current
480 * 'cryptmethod' when 'key' is changed.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200481 */
482 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100483ml_set_crypt_key(
484 buf_T *buf,
485 char_u *old_key,
486 char_u *old_cm)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200487{
488 memfile_T *mfp = buf->b_ml.ml_mfp;
489 bhdr_T *hp;
490 int page_count;
491 int idx;
492 long error;
493 infoptr_T *ip;
494 PTR_BL *pp;
495 DATA_BL *dp;
496 blocknr_T bnum;
497 int top;
Bram Moolenaarbc563362015-06-09 18:35:25 +0200498 int old_method;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200499
Christian Brabandtf573c6e2021-06-20 14:02:16 +0200500 if (mfp == NULL || mfp->mf_fd < 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100501 return; // no memfile yet, nothing to do
Bram Moolenaarbc563362015-06-09 18:35:25 +0200502 old_method = crypt_method_nr_from_name(old_cm);
503
Bram Moolenaare1b48222023-04-24 18:11:35 +0100504 // Swapfile encryption is not supported by XChaCha20, therefore disable the
505 // swapfile to avoid plain text being written to disk.
506 if (crypt_method_is_sodium(crypt_get_method_nr(buf))
507 && *buf->b_p_key != NUL)
Christian Brabandtf573c6e2021-06-20 14:02:16 +0200508 {
509 // close the swapfile
510 mf_close_file(buf, TRUE);
511 buf->b_p_swf = FALSE;
512 return;
513 }
Bram Moolenaare1b48222023-04-24 18:11:35 +0100514
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100515 // First make sure the swapfile is in a consistent state, using the old
516 // key and method.
Bram Moolenaarbc563362015-06-09 18:35:25 +0200517 {
518 char_u *new_key = buf->b_p_key;
519 char_u *new_buf_cm = buf->b_p_cm;
520
521 buf->b_p_key = old_key;
522 buf->b_p_cm = old_cm;
523 ml_preserve(buf, FALSE);
524 buf->b_p_key = new_key;
525 buf->b_p_cm = new_buf_cm;
526 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200527
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100528 // Set the key, method and seed to be used for reading, these must be the
529 // old values.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200530 mfp->mf_old_key = old_key;
Bram Moolenaarbc563362015-06-09 18:35:25 +0200531 mfp->mf_old_cm = old_method;
532 if (old_method > 0 && *old_key != NUL)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200533 mch_memmove(mfp->mf_old_seed, mfp->mf_seed, MF_SEED_LEN);
534
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100535 // Update block 0 with the crypt flag and may set a new seed.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200536 ml_upd_block0(buf, UB_CRYPT);
537
538 if (mfp->mf_infile_count > 2)
539 {
540 /*
541 * Need to read back all data blocks from disk, decrypt them with the
542 * old key/method and mark them to be written. The algorithm is
543 * similar to what happens in ml_recover(), but we skip negative block
544 * numbers.
545 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100546 ml_flush_line(buf); // flush buffered line
547 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush locked block
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200548
549 hp = NULL;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100550 bnum = 1; // start with block 1
551 page_count = 1; // which is 1 page
552 idx = 0; // start with first index in block 1
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200553 error = 0;
554 buf->b_ml.ml_stack_top = 0;
Bram Moolenaard23a8232018-02-10 18:45:26 +0100555 VIM_CLEAR(buf->b_ml.ml_stack);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100556 buf->b_ml.ml_stack_size = 0; // no stack yet
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200557
558 for ( ; !got_int; line_breakcheck())
559 {
560 if (hp != NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100561 mf_put(mfp, hp, FALSE, FALSE); // release previous block
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200562
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100563 // get the block (pointer or data)
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +0000564 if ((hp = mf_get(mfp, bnum, page_count)) == NULL)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200565 {
566 if (bnum == 1)
567 break;
568 ++error;
569 }
570 else
571 {
572 pp = (PTR_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100573 if (pp->pb_id == PTR_ID) // it is a pointer block
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200574 {
575 if (pp->pb_count == 0)
576 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100577 // empty block?
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200578 ++error;
579 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100580 else if (idx < (int)pp->pb_count) // go a block deeper
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200581 {
582 if (pp->pb_pointer[idx].pe_bnum < 0)
583 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100584 // Skip data block with negative block number.
585 // Should not happen, because of the ml_preserve()
586 // above. Get same block again for next index.
Bram Moolenaar4b7214e2019-01-03 21:55:32 +0100587 ++idx;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200588 continue;
589 }
590
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100591 // going one block deeper in the tree, new entry in
592 // stack
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200593 if ((top = ml_add_stack(buf)) < 0)
594 {
595 ++error;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100596 break; // out of memory
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200597 }
598 ip = &(buf->b_ml.ml_stack[top]);
599 ip->ip_bnum = bnum;
600 ip->ip_index = idx;
601
602 bnum = pp->pb_pointer[idx].pe_bnum;
603 page_count = pp->pb_pointer[idx].pe_page_count;
Bram Moolenaarbc563362015-06-09 18:35:25 +0200604 idx = 0;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200605 continue;
606 }
607 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100608 else // not a pointer block
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200609 {
610 dp = (DATA_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100611 if (dp->db_id != DATA_ID) // block id wrong
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200612 ++error;
613 else
614 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100615 // It is a data block, need to write it back to disk.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200616 mf_put(mfp, hp, TRUE, FALSE);
617 hp = NULL;
618 }
619 }
620 }
621
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100622 if (buf->b_ml.ml_stack_top == 0) // finished
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200623 break;
624
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100625 // go one block up in the tree
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200626 ip = &(buf->b_ml.ml_stack[--(buf->b_ml.ml_stack_top)]);
627 bnum = ip->ip_bnum;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100628 idx = ip->ip_index + 1; // go to next index
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200629 page_count = 1;
630 }
Bram Moolenaarbc563362015-06-09 18:35:25 +0200631 if (hp != NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100632 mf_put(mfp, hp, FALSE, FALSE); // release previous block
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +0100633
634 if (error > 0)
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +0000635 emsg(_(e_error_while_updating_swap_file_crypt));
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200636 }
637
638 mfp->mf_old_key = NULL;
639}
640#endif
641
Bram Moolenaar071d4272004-06-13 20:20:40 +0000642/*
643 * ml_setname() is called when the file name of "buf" has been changed.
644 * It may rename the swap file.
645 */
646 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100647ml_setname(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000648{
649 int success = FALSE;
650 memfile_T *mfp;
651 char_u *fname;
652 char_u *dirp;
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100653#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000654 char_u *p;
655#endif
656
657 mfp = buf->b_ml.ml_mfp;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100658 if (mfp->mf_fd < 0) // there is no swap file yet
Bram Moolenaar071d4272004-06-13 20:20:40 +0000659 {
660 /*
661 * When 'updatecount' is 0 and 'noswapfile' there is no swap file.
662 * For help files we will make a swap file now.
663 */
Bram Moolenaare1004402020-10-24 20:49:43 +0200664 if (p_uc != 0 && (cmdmod.cmod_flags & CMOD_NOSWAPFILE) == 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100665 ml_open_file(buf); // create a swap file
Bram Moolenaar071d4272004-06-13 20:20:40 +0000666 return;
667 }
668
669 /*
670 * Try all directories in the 'directory' option.
671 */
672 dirp = p_dir;
673 for (;;)
674 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100675 if (*dirp == NUL) // tried all directories, fail
Bram Moolenaar071d4272004-06-13 20:20:40 +0000676 break;
Bram Moolenaar8fc061c2004-12-29 21:03:02 +0000677 fname = findswapname(buf, &dirp, mfp->mf_fname);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100678 // alloc's fname
679 if (dirp == NULL) // out of memory
Bram Moolenaarf541c362011-10-26 11:44:18 +0200680 break;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100681 if (fname == NULL) // no file name found for this dir
Bram Moolenaar071d4272004-06-13 20:20:40 +0000682 continue;
683
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100684#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000685 /*
686 * Set full pathname for swap file now, because a ":!cd dir" may
687 * change directory without us knowing it.
688 */
689 p = FullName_save(fname, FALSE);
690 vim_free(fname);
691 fname = p;
692 if (fname == NULL)
693 continue;
694#endif
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100695 // if the file name is the same we don't have to do anything
Bram Moolenaar071d4272004-06-13 20:20:40 +0000696 if (fnamecmp(fname, mfp->mf_fname) == 0)
697 {
698 vim_free(fname);
699 success = TRUE;
700 break;
701 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100702 // need to close the swap file before renaming
Bram Moolenaar071d4272004-06-13 20:20:40 +0000703 if (mfp->mf_fd >= 0)
704 {
705 close(mfp->mf_fd);
706 mfp->mf_fd = -1;
707 }
708
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100709 // try to rename the swap file
Bram Moolenaar071d4272004-06-13 20:20:40 +0000710 if (vim_rename(mfp->mf_fname, fname) == 0)
711 {
712 success = TRUE;
713 vim_free(mfp->mf_fname);
714 mfp->mf_fname = fname;
715 vim_free(mfp->mf_ffname);
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100716#if defined(MSWIN)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100717 mfp->mf_ffname = NULL; // mf_fname is full pathname already
Bram Moolenaar071d4272004-06-13 20:20:40 +0000718#else
719 mf_set_ffname(mfp);
720#endif
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200721 ml_upd_block0(buf, UB_SAME_DIR);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000722 break;
723 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100724 vim_free(fname); // this fname didn't work, try another
Bram Moolenaar071d4272004-06-13 20:20:40 +0000725 }
726
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100727 if (mfp->mf_fd == -1) // need to (re)open the swap file
Bram Moolenaar071d4272004-06-13 20:20:40 +0000728 {
729 mfp->mf_fd = mch_open((char *)mfp->mf_fname, O_RDWR | O_EXTRA, 0);
730 if (mfp->mf_fd < 0)
731 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100732 // could not (re)open the swap file, what can we do????
Bram Moolenaareaaac012022-01-02 17:00:40 +0000733 emsg(_(e_oops_lost_the_swap_file));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000734 return;
735 }
Bram Moolenaarf05da212009-11-17 16:13:15 +0000736#ifdef HAVE_FD_CLOEXEC
737 {
738 int fdflags = fcntl(mfp->mf_fd, F_GETFD);
739 if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0)
Bram Moolenaarfbc4b4d2016-02-07 15:14:01 +0100740 (void)fcntl(mfp->mf_fd, F_SETFD, fdflags | FD_CLOEXEC);
Bram Moolenaarf05da212009-11-17 16:13:15 +0000741 }
742#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000743 }
744 if (!success)
Bram Moolenaareaaac012022-01-02 17:00:40 +0000745 emsg(_(e_could_not_rename_swap_file));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000746}
747
748/*
749 * Open a file for the memfile for all buffers that are not readonly or have
750 * been modified.
751 * Used when 'updatecount' changes from zero to non-zero.
752 */
753 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100754ml_open_files(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000755{
756 buf_T *buf;
757
Bram Moolenaar29323592016-07-24 22:04:11 +0200758 FOR_ALL_BUFFERS(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000759 if (!buf->b_p_ro || buf->b_changed)
760 ml_open_file(buf);
761}
762
763/*
764 * Open a swap file for an existing memfile, if there is no swap file yet.
765 * If we are unable to find a file name, mf_fname will be NULL
766 * and the memfile will be in memory only (no recovery possible).
767 */
768 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100769ml_open_file(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000770{
771 memfile_T *mfp;
772 char_u *fname;
773 char_u *dirp;
774
775 mfp = buf->b_ml.ml_mfp;
Bram Moolenaare1004402020-10-24 20:49:43 +0200776 if (mfp == NULL || mfp->mf_fd >= 0 || !buf->b_p_swf
777 || (cmdmod.cmod_flags & CMOD_NOSWAPFILE))
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100778 return; // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +0000779
Bram Moolenaara1956f62006-03-12 22:18:00 +0000780#ifdef FEAT_SPELL
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100781 // For a spell buffer use a temp file name.
Bram Moolenaar4770d092006-01-12 23:22:24 +0000782 if (buf->b_spell)
783 {
Bram Moolenaare5c421c2015-03-31 13:33:08 +0200784 fname = vim_tempname('s', FALSE);
Bram Moolenaar4770d092006-01-12 23:22:24 +0000785 if (fname != NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100786 (void)mf_open_file(mfp, fname); // consumes fname!
Bram Moolenaar4770d092006-01-12 23:22:24 +0000787 buf->b_may_swap = FALSE;
788 return;
789 }
790#endif
791
Bram Moolenaar071d4272004-06-13 20:20:40 +0000792 /*
793 * Try all directories in 'directory' option.
794 */
795 dirp = p_dir;
796 for (;;)
797 {
798 if (*dirp == NUL)
799 break;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100800 // There is a small chance that between choosing the swap file name
801 // and creating it, another Vim creates the file. In that case the
802 // creation will fail and we will use another directory.
803 fname = findswapname(buf, &dirp, NULL); // allocates fname
Bram Moolenaarf541c362011-10-26 11:44:18 +0200804 if (dirp == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100805 break; // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +0000806 if (fname == NULL)
807 continue;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100808 if (mf_open_file(mfp, fname) == OK) // consumes fname!
Bram Moolenaar071d4272004-06-13 20:20:40 +0000809 {
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100810#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000811 /*
812 * set full pathname for swap file now, because a ":!cd dir" may
813 * change directory without us knowing it.
814 */
815 mf_fullname(mfp);
816#endif
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200817 ml_upd_block0(buf, UB_SAME_DIR);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000818
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100819 // Flush block zero, so others can read it
Bram Moolenaar071d4272004-06-13 20:20:40 +0000820 if (mf_sync(mfp, MFS_ZERO) == OK)
Bram Moolenaarc32840f2006-01-14 21:23:38 +0000821 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100822 // Mark all blocks that should be in the swapfile as dirty.
823 // Needed for when the 'swapfile' option was reset, so that
824 // the swap file was deleted, and then on again.
Bram Moolenaarc32840f2006-01-14 21:23:38 +0000825 mf_set_dirty(mfp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000826 break;
Bram Moolenaarc32840f2006-01-14 21:23:38 +0000827 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100828 // Writing block 0 failed: close the file and try another dir
Bram Moolenaar071d4272004-06-13 20:20:40 +0000829 mf_close_file(buf, FALSE);
830 }
831 }
832
Bram Moolenaar00e192b2019-10-19 17:01:28 +0200833 if (*p_dir != NUL && mfp->mf_fname == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000834 {
Bram Moolenaar13608d82022-08-29 15:06:50 +0100835 need_wait_return = TRUE; // call wait_return() later
Bram Moolenaar071d4272004-06-13 20:20:40 +0000836 ++no_wait_return;
Bram Moolenaareaaac012022-01-02 17:00:40 +0000837 (void)semsg(_(e_unable_to_open_swap_file_for_str_recovery_impossible),
Bram Moolenaare1704ba2012-10-03 18:25:00 +0200838 buf_spname(buf) != NULL ? buf_spname(buf) : buf->b_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000839 --no_wait_return;
840 }
841
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100842 // don't try to open a swap file again
Bram Moolenaar071d4272004-06-13 20:20:40 +0000843 buf->b_may_swap = FALSE;
844}
845
846/*
847 * If still need to create a swap file, and starting to edit a not-readonly
848 * file, or reading into an existing buffer, create a swap file now.
849 */
850 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100851check_need_swap(
Bram Moolenaar2f0f8712018-08-21 18:50:18 +0200852 int newfile) // reading file into new buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +0000853{
Bram Moolenaar2f0f8712018-08-21 18:50:18 +0200854 int old_msg_silent = msg_silent; // might be reset by an E325 message
855
Bram Moolenaar071d4272004-06-13 20:20:40 +0000856 if (curbuf->b_may_swap && (!curbuf->b_p_ro || !newfile))
857 ml_open_file(curbuf);
Bram Moolenaar2f0f8712018-08-21 18:50:18 +0200858 msg_silent = old_msg_silent;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000859}
860
861/*
862 * Close memline for buffer 'buf'.
863 * If 'del_file' is TRUE, delete the swap file
864 */
865 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100866ml_close(buf_T *buf, int del_file)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000867{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100868 if (buf->b_ml.ml_mfp == NULL) // not open
Bram Moolenaar071d4272004-06-13 20:20:40 +0000869 return;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100870 mf_close(buf->b_ml.ml_mfp, del_file); // close the .swp file
Bram Moolenaarfa4873c2022-06-30 22:13:59 +0100871 if (buf->b_ml.ml_line_lnum != 0
872 && (buf->b_ml.ml_flags & (ML_LINE_DIRTY | ML_ALLOCATED)))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000873 vim_free(buf->b_ml.ml_line_ptr);
874 vim_free(buf->b_ml.ml_stack);
875#ifdef FEAT_BYTEOFF
Bram Moolenaard23a8232018-02-10 18:45:26 +0100876 VIM_CLEAR(buf->b_ml.ml_chunksize);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000877#endif
878 buf->b_ml.ml_mfp = NULL;
879
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100880 // Reset the "recovered" flag, give the ATTENTION prompt the next time
881 // this buffer is loaded.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000882 buf->b_flags &= ~BF_RECOVERED;
883}
884
885/*
886 * Close all existing memlines and memfiles.
887 * Only used when exiting.
888 * When 'del_file' is TRUE, delete the memfiles.
Bram Moolenaar81bf7082005-02-12 14:31:42 +0000889 * But don't delete files that were ":preserve"d when we are POSIX compatible.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000890 */
891 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100892ml_close_all(int del_file)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000893{
894 buf_T *buf;
895
Bram Moolenaar29323592016-07-24 22:04:11 +0200896 FOR_ALL_BUFFERS(buf)
Bram Moolenaar81bf7082005-02-12 14:31:42 +0000897 ml_close(buf, del_file && ((buf->b_flags & BF_PRESERVED) == 0
898 || vim_strchr(p_cpo, CPO_PRESERVE) == NULL));
Bram Moolenaar34b466e2013-11-28 17:41:46 +0100899#ifdef FEAT_SPELL
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100900 spell_delete_wordlist(); // delete the internal wordlist
Bram Moolenaar34b466e2013-11-28 17:41:46 +0100901#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000902#ifdef TEMPDIRNAMES
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100903 vim_deltempdir(); // delete created temp directory
Bram Moolenaar071d4272004-06-13 20:20:40 +0000904#endif
905}
906
907/*
908 * Close all memfiles for not modified buffers.
909 * Only use just before exiting!
910 */
911 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100912ml_close_notmod(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000913{
914 buf_T *buf;
915
Bram Moolenaar29323592016-07-24 22:04:11 +0200916 FOR_ALL_BUFFERS(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000917 if (!bufIsChanged(buf))
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100918 ml_close(buf, TRUE); // close all not-modified buffers
Bram Moolenaar071d4272004-06-13 20:20:40 +0000919}
920
921/*
922 * Update the timestamp in the .swp file.
923 * Used when the file has been written.
924 */
925 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100926ml_timestamp(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000927{
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200928 ml_upd_block0(buf, UB_FNAME);
929}
930
931/*
932 * Return FAIL when the ID of "b0p" is wrong.
933 */
934 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100935ml_check_b0_id(ZERO_BL *b0p)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200936{
937 if (b0p->b0_id[0] != BLOCK0_ID0
938 || (b0p->b0_id[1] != BLOCK0_ID1
939 && b0p->b0_id[1] != BLOCK0_ID1_C0
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200940 && b0p->b0_id[1] != BLOCK0_ID1_C1
Christian Brabandtf573c6e2021-06-20 14:02:16 +0200941 && b0p->b0_id[1] != BLOCK0_ID1_C2
942 && b0p->b0_id[1] != BLOCK0_ID1_C3)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200943 )
944 return FAIL;
945 return OK;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000946}
947
948/*
949 * Update the timestamp or the B0_SAME_DIR flag of the .swp file.
950 */
951 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100952ml_upd_block0(buf_T *buf, upd_block0_T what)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000953{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000954 memfile_T *mfp;
955 bhdr_T *hp;
956 ZERO_BL *b0p;
957
958 mfp = buf->b_ml.ml_mfp;
Bram Moolenaar2be79502014-08-13 21:58:28 +0200959 if (mfp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000960 return;
Bram Moolenaar2be79502014-08-13 21:58:28 +0200961 hp = mf_get(mfp, (blocknr_T)0, 1);
962 if (hp == NULL)
963 {
964#ifdef FEAT_CRYPT
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100965 // Possibly update the seed in the memfile before there is a block0.
Bram Moolenaar2be79502014-08-13 21:58:28 +0200966 if (what == UB_CRYPT)
967 ml_set_mfp_crypt(buf);
968#endif
969 return;
970 }
971
Bram Moolenaar071d4272004-06-13 20:20:40 +0000972 b0p = (ZERO_BL *)(hp->bh_data);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200973 if (ml_check_b0_id(b0p) == FAIL)
Bram Moolenaareaaac012022-01-02 17:00:40 +0000974 iemsg(_(e_ml_upd_block0_didnt_get_block_zero));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000975 else
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000976 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200977 if (what == UB_FNAME)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000978 set_b0_fname(b0p, buf);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200979#ifdef FEAT_CRYPT
980 else if (what == UB_CRYPT)
981 ml_set_b0_crypt(buf, b0p);
982#endif
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100983 else // what == UB_SAME_DIR
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000984 set_b0_dir_flag(b0p, buf);
985 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000986 mf_put(mfp, hp, TRUE, FALSE);
987}
988
989/*
990 * Write file name and timestamp into block 0 of a swap file.
991 * Also set buf->b_mtime.
992 * Don't use NameBuff[]!!!
993 */
994 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100995set_b0_fname(ZERO_BL *b0p, buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000996{
Bram Moolenaar8767f522016-07-01 17:17:39 +0200997 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000998
999 if (buf->b_ffname == NULL)
1000 b0p->b0_fname[0] = NUL;
1001 else
1002 {
Bram Moolenaar48e330a2016-02-23 14:53:34 +01001003#if defined(MSWIN) || defined(AMIGA)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001004 // Systems that cannot translate "~user" back into a path: copy the
1005 // file name unmodified. Do use slashes instead of backslashes for
1006 // portability.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001007 vim_strncpy(b0p->b0_fname, buf->b_ffname, B0_FNAME_SIZE_CRYPT - 1);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001008# ifdef BACKSLASH_IN_FILENAME
1009 forward_slash(b0p->b0_fname);
1010# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001011#else
1012 size_t flen, ulen;
1013 char_u uname[B0_UNAME_SIZE];
1014
1015 /*
1016 * For a file under the home directory of the current user, we try to
1017 * replace the home directory path with "~user". This helps when
1018 * editing the same file on different machines over a network.
1019 * First replace home dir path with "~/" with home_replace().
1020 * Then insert the user name to get "~user/".
1021 */
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001022 home_replace(NULL, buf->b_ffname, b0p->b0_fname,
1023 B0_FNAME_SIZE_CRYPT, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001024 if (b0p->b0_fname[0] == '~')
1025 {
1026 flen = STRLEN(b0p->b0_fname);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001027 // If there is no user name or it is too long, don't use "~/"
Bram Moolenaar071d4272004-06-13 20:20:40 +00001028 if (get_user_name(uname, B0_UNAME_SIZE) == FAIL
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001029 || (ulen = STRLEN(uname)) + flen > B0_FNAME_SIZE_CRYPT - 1)
1030 vim_strncpy(b0p->b0_fname, buf->b_ffname,
1031 B0_FNAME_SIZE_CRYPT - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001032 else
1033 {
1034 mch_memmove(b0p->b0_fname + ulen + 1, b0p->b0_fname + 1, flen);
1035 mch_memmove(b0p->b0_fname + 1, uname, ulen);
1036 }
1037 }
1038#endif
1039 if (mch_stat((char *)buf->b_ffname, &st) >= 0)
1040 {
1041 long_to_char((long)st.st_mtime, b0p->b0_mtime);
1042#ifdef CHECK_INODE
1043 long_to_char((long)st.st_ino, b0p->b0_ino);
1044#endif
1045 buf_store_time(buf, &st, buf->b_ffname);
1046 buf->b_mtime_read = buf->b_mtime;
Leah Neukirchen0a7984a2021-10-14 21:27:55 +01001047 buf->b_mtime_read_ns = buf->b_mtime_ns;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001048 }
1049 else
1050 {
1051 long_to_char(0L, b0p->b0_mtime);
1052#ifdef CHECK_INODE
1053 long_to_char(0L, b0p->b0_ino);
1054#endif
1055 buf->b_mtime = 0;
Leah Neukirchen0a7984a2021-10-14 21:27:55 +01001056 buf->b_mtime_ns = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001057 buf->b_mtime_read = 0;
Leah Neukirchen0a7984a2021-10-14 21:27:55 +01001058 buf->b_mtime_read_ns = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001059 buf->b_orig_size = 0;
1060 buf->b_orig_mode = 0;
1061 }
1062 }
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001063
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001064 // Also add the 'fileencoding' if there is room.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001065 add_b0_fenc(b0p, curbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001066}
1067
1068/*
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001069 * Update the B0_SAME_DIR flag of the swap file. It's set if the file and the
1070 * swapfile for "buf" are in the same directory.
1071 * This is fail safe: if we are not sure the directories are equal the flag is
1072 * not set.
1073 */
1074 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001075set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001076{
1077 if (same_directory(buf->b_ml.ml_mfp->mf_fname, buf->b_ffname))
1078 b0p->b0_flags |= B0_SAME_DIR;
1079 else
1080 b0p->b0_flags &= ~B0_SAME_DIR;
1081}
1082
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001083/*
1084 * When there is room, add the 'fileencoding' to block zero.
1085 */
1086 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001087add_b0_fenc(
1088 ZERO_BL *b0p,
1089 buf_T *buf)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001090{
1091 int n;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001092 int size = B0_FNAME_SIZE_NOCRYPT;
1093
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01001094#ifdef FEAT_CRYPT
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001095 // Without encryption use the same offset as in Vim 7.2 to be compatible.
1096 // With encryption it's OK to move elsewhere, the swap file is not
1097 // compatible anyway.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001098 if (*buf->b_p_key != NUL)
1099 size = B0_FNAME_SIZE_CRYPT;
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01001100#endif
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001101
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00001102 n = (int)STRLEN(buf->b_p_fenc);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001103 if ((int)STRLEN(b0p->b0_fname) + n + 1 > size)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001104 b0p->b0_flags &= ~B0_HAS_FENC;
1105 else
1106 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001107 mch_memmove((char *)b0p->b0_fname + size - n,
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001108 (char *)buf->b_p_fenc, (size_t)n);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001109 *(b0p->b0_fname + size - n - 1) = NUL;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001110 b0p->b0_flags |= B0_HAS_FENC;
1111 }
1112}
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001113
Bram Moolenaarf52f0602021-03-10 21:26:37 +01001114#if defined(HAVE_SYS_SYSINFO_H) && defined(HAVE_SYSINFO_UPTIME)
1115# include <sys/sysinfo.h>
1116#endif
1117
=?UTF-8?q?Ola=20S=C3=B6der?=599a6e52021-07-06 20:15:46 +02001118#if defined(UNIX) || defined(MSWIN)
Bram Moolenaarf52f0602021-03-10 21:26:37 +01001119/*
1120 * Return TRUE if the process with number "b0p->b0_pid" is still running.
1121 * "swap_fname" is the name of the swap file, if it's from before a reboot then
1122 * the result is FALSE;
1123 */
1124 static int
1125swapfile_process_running(ZERO_BL *b0p, char_u *swap_fname UNUSED)
1126{
Bram Moolenaare2982d62021-10-06 11:27:21 +01001127#if defined(HAVE_SYSINFO) && defined(HAVE_SYSINFO_UPTIME)
Bram Moolenaarf52f0602021-03-10 21:26:37 +01001128 stat_T st;
1129 struct sysinfo sinfo;
1130
1131 // If the system rebooted after when the swap file was written then the
1132 // process can't be running now.
1133 if (mch_stat((char *)swap_fname, &st) != -1
1134 && sysinfo(&sinfo) == 0
Bram Moolenaar23b32a82021-03-10 21:55:46 +01001135 && st.st_mtime < time(NULL) - (
=?UTF-8?q?Ola=20S=C3=B6der?=599a6e52021-07-06 20:15:46 +02001136# ifdef FEAT_EVAL
Bram Moolenaar23b32a82021-03-10 21:55:46 +01001137 override_sysinfo_uptime >= 0 ? override_sysinfo_uptime :
=?UTF-8?q?Ola=20S=C3=B6der?=599a6e52021-07-06 20:15:46 +02001138# endif
Bram Moolenaar23b32a82021-03-10 21:55:46 +01001139 sinfo.uptime))
Bram Moolenaarf52f0602021-03-10 21:26:37 +01001140 return FALSE;
=?UTF-8?q?Ola=20S=C3=B6der?=599a6e52021-07-06 20:15:46 +02001141# endif
Bram Moolenaarf52f0602021-03-10 21:26:37 +01001142 return mch_process_running(char_to_long(b0p->b0_pid));
1143}
=?UTF-8?q?Ola=20S=C3=B6der?=599a6e52021-07-06 20:15:46 +02001144#endif
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001145
1146/*
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001147 * Try to recover curbuf from the .swp file.
Bram Moolenaar99499b12019-05-23 21:35:48 +02001148 * If "checkext" is TRUE, check the extension and detect whether it is
1149 * a swap file.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001150 */
1151 void
Bram Moolenaar99499b12019-05-23 21:35:48 +02001152ml_recover(int checkext)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001153{
1154 buf_T *buf = NULL;
1155 memfile_T *mfp = NULL;
1156 char_u *fname;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001157 char_u *fname_used = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001158 bhdr_T *hp = NULL;
1159 ZERO_BL *b0p;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001160 int b0_ff;
1161 char_u *b0_fenc = NULL;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001162#ifdef FEAT_CRYPT
1163 int b0_cm = -1;
1164#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001165 PTR_BL *pp;
1166 DATA_BL *dp;
1167 infoptr_T *ip;
1168 blocknr_T bnum;
1169 int page_count;
Bram Moolenaar8767f522016-07-01 17:17:39 +02001170 stat_T org_stat, swp_stat;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001171 int len;
1172 int directly;
1173 linenr_T lnum;
1174 char_u *p;
1175 int i;
1176 long error;
1177 int cannot_open;
1178 linenr_T line_count;
1179 int has_error;
1180 int idx;
1181 int top;
1182 int txt_start;
Bram Moolenaar8767f522016-07-01 17:17:39 +02001183 off_T size;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001184 int called_from_main;
1185 int serious_error = TRUE;
1186 long mtime;
1187 int attr;
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001188 int orig_file_status = NOTDONE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001189
1190 recoverymode = TRUE;
1191 called_from_main = (curbuf->b_ml.ml_mfp == NULL);
Bram Moolenaar8820b482017-03-16 17:23:31 +01001192 attr = HL_ATTR(HLF_E);
Bram Moolenaard0ba34a2009-11-03 12:06:23 +00001193
1194 /*
Bram Moolenaaraf903e52017-12-02 15:11:22 +01001195 * 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 +00001196 * Otherwise a search is done to find the swap file(s).
1197 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001198 fname = curbuf->b_fname;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001199 if (fname == NULL) // When there is no file name
Bram Moolenaar071d4272004-06-13 20:20:40 +00001200 fname = (char_u *)"";
1201 len = (int)STRLEN(fname);
Bram Moolenaar99499b12019-05-23 21:35:48 +02001202 if (checkext && len >= 4 &&
Bram Moolenaare60acc12011-05-10 16:41:25 +02001203#if defined(VMS)
Bram Moolenaar79518e22017-02-17 16:31:35 +01001204 STRNICMP(fname + len - 4, "_s", 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001205#else
Bram Moolenaar79518e22017-02-17 16:31:35 +01001206 STRNICMP(fname + len - 4, ".s", 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001207#endif
Bram Moolenaar79518e22017-02-17 16:31:35 +01001208 == 0
Bram Moolenaaraf903e52017-12-02 15:11:22 +01001209 && vim_strchr((char_u *)"abcdefghijklmnopqrstuvw",
1210 TOLOWER_ASC(fname[len - 2])) != NULL
Bram Moolenaard0ba34a2009-11-03 12:06:23 +00001211 && ASCII_ISALPHA(fname[len - 1]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001212 {
1213 directly = TRUE;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001214 fname_used = vim_strsave(fname); // make a copy for mf_open()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001215 }
1216 else
1217 {
1218 directly = FALSE;
1219
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001220 // count the number of matching swap files
Bram Moolenaarc216a7a2022-12-05 13:50:55 +00001221 len = recover_names(fname, FALSE, NULL, 0, NULL);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001222 if (len == 0) // no swap files found
Bram Moolenaar071d4272004-06-13 20:20:40 +00001223 {
Bram Moolenaareaaac012022-01-02 17:00:40 +00001224 semsg(_(e_no_swap_file_found_for_str), fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001225 goto theend;
1226 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001227 if (len == 1) // one swap file found, use it
Bram Moolenaar071d4272004-06-13 20:20:40 +00001228 i = 1;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001229 else // several swap files found, choose
Bram Moolenaar071d4272004-06-13 20:20:40 +00001230 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001231 // list the names of the swap files
Bram Moolenaarc216a7a2022-12-05 13:50:55 +00001232 (void)recover_names(fname, TRUE, NULL, 0, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001233 msg_putchar('\n');
Bram Moolenaar32526b32019-01-19 17:43:09 +01001234 msg_puts(_("Enter number of swap file to use (0 to quit): "));
Bram Moolenaar24bbcfe2005-06-28 23:32:02 +00001235 i = get_number(FALSE, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001236 if (i < 1 || i > len)
1237 goto theend;
1238 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001239 // get the swap file name that will be used
Bram Moolenaarc216a7a2022-12-05 13:50:55 +00001240 (void)recover_names(fname, FALSE, NULL, i, &fname_used);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001241 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001242 if (fname_used == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001243 goto theend; // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00001244
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001245 // When called from main() still need to initialize storage structure
Bram Moolenaar4770d092006-01-12 23:22:24 +00001246 if (called_from_main && ml_open(curbuf) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001247 getout(1);
1248
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001249 /*
1250 * Allocate a buffer structure for the swap file that is used for recovery.
Bram Moolenaar0ad014c2010-07-25 14:00:46 +02001251 * Only the memline and crypt information in it are really used.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001252 */
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001253 buf = ALLOC_ONE(buf_T);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001254 if (buf == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001255 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001256
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001257 /*
1258 * init fields in memline struct
1259 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001260 buf->b_ml.ml_stack_size = 0; // no stack yet
1261 buf->b_ml.ml_stack = NULL; // no stack yet
1262 buf->b_ml.ml_stack_top = 0; // nothing in the stack
1263 buf->b_ml.ml_line_lnum = 0; // no cached line
1264 buf->b_ml.ml_locked = NULL; // no locked block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001265 buf->b_ml.ml_flags = 0;
Bram Moolenaar0fe849a2010-07-25 15:11:11 +02001266#ifdef FEAT_CRYPT
1267 buf->b_p_key = empty_option;
1268 buf->b_p_cm = empty_option;
1269#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001270
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001271 /*
1272 * open the memfile from the old swap file
1273 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001274 p = vim_strsave(fname_used); // save "fname_used" for the message:
1275 // mf_open() will consume "fname_used"!
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001276 mfp = mf_open(fname_used, O_RDONLY);
1277 fname_used = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001278 if (mfp == NULL || mfp->mf_fd < 0)
1279 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001280 if (fname_used != NULL)
Bram Moolenaareaaac012022-01-02 17:00:40 +00001281 semsg(_(e_cannot_open_str), fname_used);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001282 goto theend;
1283 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001284 buf->b_ml.ml_mfp = mfp;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001285#ifdef FEAT_CRYPT
1286 mfp->mf_buffer = buf;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001287#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001288
1289 /*
1290 * The page size set in mf_open() might be different from the page size
1291 * used in the swap file, we must get it from block 0. But to read block
1292 * 0 we need a page size. Use the minimal size for block 0 here, it will
1293 * be set to the real value below.
1294 */
1295 mfp->mf_page_size = MIN_SWAP_PAGE_SIZE;
1296
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001297 /*
1298 * try to read block 0
1299 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001300 if ((hp = mf_get(mfp, (blocknr_T)0, 1)) == NULL)
1301 {
1302 msg_start();
Bram Moolenaar32526b32019-01-19 17:43:09 +01001303 msg_puts_attr(_("Unable to read block 0 from "), attr | MSG_HIST);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001304 msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001305 msg_puts_attr(_("\nMaybe no changes were made or Vim did not update the swap file."),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001306 attr | MSG_HIST);
1307 msg_end();
1308 goto theend;
1309 }
1310 b0p = (ZERO_BL *)(hp->bh_data);
1311 if (STRNCMP(b0p->b0_version, "VIM 3.0", 7) == 0)
1312 {
1313 msg_start();
1314 msg_outtrans_attr(mfp->mf_fname, MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001315 msg_puts_attr(_(" cannot be used with this version of Vim.\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001316 MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001317 msg_puts_attr(_("Use Vim version 3.0.\n"), MSG_HIST);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001318 msg_end();
1319 goto theend;
1320 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001321 if (ml_check_b0_id(b0p) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001322 {
Bram Moolenaareaaac012022-01-02 17:00:40 +00001323 semsg(_(e_str_does_not_look_like_vim_swap_file), mfp->mf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001324 goto theend;
1325 }
1326 if (b0_magic_wrong(b0p))
1327 {
1328 msg_start();
1329 msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
Bram Moolenaar48e330a2016-02-23 14:53:34 +01001330#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001331 if (STRNCMP(b0p->b0_hname, "PC ", 3) == 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001332 msg_puts_attr(_(" cannot be used with this version of Vim.\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001333 attr | MSG_HIST);
1334 else
1335#endif
Bram Moolenaar32526b32019-01-19 17:43:09 +01001336 msg_puts_attr(_(" cannot be used on this computer.\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001337 attr | MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001338 msg_puts_attr(_("The file was created on "), attr | MSG_HIST);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001339 // avoid going past the end of a corrupted hostname
Bram Moolenaar071d4272004-06-13 20:20:40 +00001340 b0p->b0_fname[0] = NUL;
Bram Moolenaar32526b32019-01-19 17:43:09 +01001341 msg_puts_attr((char *)b0p->b0_hname, attr | MSG_HIST);
1342 msg_puts_attr(_(",\nor the file has been damaged."), attr | MSG_HIST);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001343 msg_end();
1344 goto theend;
1345 }
Bram Moolenaar1c536282007-04-26 15:21:56 +00001346
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001347#ifdef FEAT_CRYPT
K.Takataeeec2542021-06-02 13:28:16 +02001348 for (i = 0; i < (int)ARRAY_LENGTH(id1_codes); ++i)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001349 if (id1_codes[i] == b0p->b0_id[1])
1350 b0_cm = i;
1351 if (b0_cm > 0)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001352 mch_memmove(mfp->mf_seed, &b0p->b0_seed, MF_SEED_LEN);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001353 crypt_set_cm_option(buf, b0_cm < 0 ? 0 : b0_cm);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001354#else
1355 if (b0p->b0_id[1] != BLOCK0_ID1)
1356 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00001357 semsg(_(e_str_is_encrypted_and_this_version_of_vim_does_not_support_encryption), mfp->mf_fname);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001358 goto theend;
1359 }
1360#endif
1361
Bram Moolenaar071d4272004-06-13 20:20:40 +00001362 /*
1363 * If we guessed the wrong page size, we have to recalculate the
1364 * highest block number in the file.
1365 */
1366 if (mfp->mf_page_size != (unsigned)char_to_long(b0p->b0_page_size))
1367 {
Bram Moolenaar1c536282007-04-26 15:21:56 +00001368 unsigned previous_page_size = mfp->mf_page_size;
1369
Bram Moolenaar071d4272004-06-13 20:20:40 +00001370 mf_new_page_size(mfp, (unsigned)char_to_long(b0p->b0_page_size));
Bram Moolenaar1c536282007-04-26 15:21:56 +00001371 if (mfp->mf_page_size < previous_page_size)
1372 {
1373 msg_start();
1374 msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001375 msg_puts_attr(_(" has been damaged (page size is smaller than minimum value).\n"),
Bram Moolenaar1c536282007-04-26 15:21:56 +00001376 attr | MSG_HIST);
1377 msg_end();
1378 goto theend;
1379 }
Bram Moolenaar8767f522016-07-01 17:17:39 +02001380 if ((size = vim_lseek(mfp->mf_fd, (off_T)0L, SEEK_END)) <= 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001381 mfp->mf_blocknr_max = 0; // no file or empty file
Bram Moolenaar071d4272004-06-13 20:20:40 +00001382 else
1383 mfp->mf_blocknr_max = (blocknr_T)(size / mfp->mf_page_size);
1384 mfp->mf_infile_count = mfp->mf_blocknr_max;
Bram Moolenaar1c536282007-04-26 15:21:56 +00001385
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001386 // need to reallocate the memory used to store the data
Bram Moolenaar1c536282007-04-26 15:21:56 +00001387 p = alloc(mfp->mf_page_size);
1388 if (p == NULL)
1389 goto theend;
1390 mch_memmove(p, hp->bh_data, previous_page_size);
1391 vim_free(hp->bh_data);
1392 hp->bh_data = p;
1393 b0p = (ZERO_BL *)(hp->bh_data);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001394 }
1395
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001396 /*
1397 * If .swp file name given directly, use name from swap file for buffer.
1398 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001399 if (directly)
1400 {
1401 expand_env(b0p->b0_fname, NameBuff, MAXPATHL);
1402 if (setfname(curbuf, NameBuff, NULL, TRUE) == FAIL)
1403 goto theend;
1404 }
1405
1406 home_replace(NULL, mfp->mf_fname, NameBuff, MAXPATHL, TRUE);
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001407 smsg(_("Using swap file \"%s\""), NameBuff);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001408
1409 if (buf_spname(curbuf) != NULL)
Bram Moolenaare1704ba2012-10-03 18:25:00 +02001410 vim_strncpy(NameBuff, buf_spname(curbuf), MAXPATHL - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001411 else
1412 home_replace(NULL, curbuf->b_ffname, NameBuff, MAXPATHL, TRUE);
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001413 smsg(_("Original file \"%s\""), NameBuff);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001414 msg_putchar('\n');
1415
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001416 /*
1417 * check date of swap file and original file
1418 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001419 mtime = char_to_long(b0p->b0_mtime);
1420 if (curbuf->b_ffname != NULL
1421 && mch_stat((char *)curbuf->b_ffname, &org_stat) != -1
1422 && ((mch_stat((char *)mfp->mf_fname, &swp_stat) != -1
1423 && org_stat.st_mtime > swp_stat.st_mtime)
1424 || org_stat.st_mtime != mtime))
Bram Moolenaareaaac012022-01-02 17:00:40 +00001425 emsg(_(e_warning_original_file_may_have_been_changed));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001426 out_flush();
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001427
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001428 // Get the 'fileformat' and 'fileencoding' from block zero.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001429 b0_ff = (b0p->b0_flags & B0_FF_MASK);
1430 if (b0p->b0_flags & B0_HAS_FENC)
1431 {
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001432 int fnsize = B0_FNAME_SIZE_NOCRYPT;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001433
1434#ifdef FEAT_CRYPT
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001435 // Use the same size as in add_b0_fenc().
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001436 if (b0p->b0_id[1] != BLOCK0_ID1)
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001437 fnsize = B0_FNAME_SIZE_CRYPT;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001438#endif
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001439 for (p = b0p->b0_fname + fnsize; p > b0p->b0_fname && p[-1] != NUL; --p)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001440 ;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001441 b0_fenc = vim_strnsave(p, b0p->b0_fname + fnsize - p);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001442 }
1443
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001444 mf_put(mfp, hp, FALSE, FALSE); // release block 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001445 hp = NULL;
1446
1447 /*
1448 * Now that we are sure that the file is going to be recovered, clear the
1449 * contents of the current buffer.
1450 */
1451 while (!(curbuf->b_ml.ml_flags & ML_EMPTY))
Bram Moolenaarca70c072020-05-30 20:30:46 +02001452 ml_delete((linenr_T)1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001453
1454 /*
1455 * Try reading the original file to obtain the values of 'fileformat',
1456 * 'fileencoding', etc. Ignore errors. The text itself is not used.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001457 * When the file is encrypted the user is asked to enter the key.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001458 */
1459 if (curbuf->b_ffname != NULL)
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001460 orig_file_status = readfile(curbuf->b_ffname, NULL, (linenr_T)0,
Bram Moolenaar071d4272004-06-13 20:20:40 +00001461 (linenr_T)0, (linenr_T)MAXLNUM, NULL, READ_NEW);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001462
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001463#ifdef FEAT_CRYPT
1464 if (b0_cm >= 0)
1465 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001466 // Need to ask the user for the crypt key. If this fails we continue
1467 // without a key, will probably get garbage text.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001468 if (*curbuf->b_p_key != NUL)
1469 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001470 smsg(_("Swap file is encrypted: \"%s\""), fname_used);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001471 msg_puts(_("\nIf you entered a new crypt key but did not write the text file,"));
1472 msg_puts(_("\nenter the new crypt key."));
1473 msg_puts(_("\nIf you wrote the text file after changing the crypt key press enter"));
1474 msg_puts(_("\nto use the same key for text file and swap file"));
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001475 }
1476 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001477 smsg(_(need_key_msg), fname_used);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001478 buf->b_p_key = crypt_get_key(FALSE, FALSE);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001479 if (buf->b_p_key == NULL)
1480 buf->b_p_key = curbuf->b_p_key;
1481 else if (*buf->b_p_key == NUL)
1482 {
1483 vim_free(buf->b_p_key);
1484 buf->b_p_key = curbuf->b_p_key;
1485 }
1486 if (buf->b_p_key == NULL)
1487 buf->b_p_key = empty_option;
1488 }
1489#endif
1490
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001491 // Use the 'fileformat' and 'fileencoding' as stored in the swap file.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001492 if (b0_ff != 0)
1493 set_fileformat(b0_ff - 1, OPT_LOCAL);
1494 if (b0_fenc != NULL)
1495 {
Bram Moolenaar31e5c602022-04-15 13:53:33 +01001496 set_option_value_give_err((char_u *)"fenc", 0L, b0_fenc, OPT_LOCAL);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001497 vim_free(b0_fenc);
1498 }
Bram Moolenaarc024b462019-06-08 18:07:21 +02001499 unchanged(curbuf, TRUE, TRUE);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001500
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001501 bnum = 1; // start with block 1
1502 page_count = 1; // which is 1 page
1503 lnum = 0; // append after line 0 in curbuf
Bram Moolenaar071d4272004-06-13 20:20:40 +00001504 line_count = 0;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001505 idx = 0; // start with first index in block 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00001506 error = 0;
1507 buf->b_ml.ml_stack_top = 0;
1508 buf->b_ml.ml_stack = NULL;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001509 buf->b_ml.ml_stack_size = 0; // no stack yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00001510
1511 if (curbuf->b_ffname == NULL)
1512 cannot_open = TRUE;
1513 else
1514 cannot_open = FALSE;
1515
1516 serious_error = FALSE;
1517 for ( ; !got_int; line_breakcheck())
1518 {
1519 if (hp != NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001520 mf_put(mfp, hp, FALSE, FALSE); // release previous block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001521
1522 /*
1523 * get block
1524 */
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00001525 if ((hp = mf_get(mfp, bnum, page_count)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001526 {
1527 if (bnum == 1)
1528 {
Bram Moolenaareaaac012022-01-02 17:00:40 +00001529 semsg(_(e_unable_to_read_block_one_from_str), mfp->mf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001530 goto theend;
1531 }
1532 ++error;
1533 ml_append(lnum++, (char_u *)_("???MANY LINES MISSING"),
1534 (colnr_T)0, TRUE);
1535 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001536 else // there is a block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001537 {
1538 pp = (PTR_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001539 if (pp->pb_id == PTR_ID) // it is a pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001540 {
Bram Moolenaarb67ba032023-04-22 21:14:26 +01001541 int ptr_block_error = FALSE;
1542 if (pp->pb_count_max != PB_COUNT_MAX(mfp))
1543 {
1544 ptr_block_error = TRUE;
1545 pp->pb_count_max = PB_COUNT_MAX(mfp);
1546 }
1547 if (pp->pb_count > pp->pb_count_max)
1548 {
1549 ptr_block_error = TRUE;
1550 pp->pb_count = pp->pb_count_max;
1551 }
1552 if (ptr_block_error)
1553 emsg(_(e_warning_pointer_block_corrupted));
1554
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001555 // check line count when using pointer block first time
Bram Moolenaar071d4272004-06-13 20:20:40 +00001556 if (idx == 0 && line_count != 0)
1557 {
1558 for (i = 0; i < (int)pp->pb_count; ++i)
1559 line_count -= pp->pb_pointer[i].pe_line_count;
1560 if (line_count != 0)
1561 {
1562 ++error;
1563 ml_append(lnum++, (char_u *)_("???LINE COUNT WRONG"),
1564 (colnr_T)0, TRUE);
1565 }
1566 }
1567
1568 if (pp->pb_count == 0)
1569 {
1570 ml_append(lnum++, (char_u *)_("???EMPTY BLOCK"),
1571 (colnr_T)0, TRUE);
1572 ++error;
1573 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001574 else if (idx < (int)pp->pb_count) // go a block deeper
Bram Moolenaar071d4272004-06-13 20:20:40 +00001575 {
1576 if (pp->pb_pointer[idx].pe_bnum < 0)
1577 {
1578 /*
1579 * Data block with negative block number.
1580 * Try to read lines from the original file.
1581 * This is slow, but it works.
1582 */
1583 if (!cannot_open)
1584 {
1585 line_count = pp->pb_pointer[idx].pe_line_count;
1586 if (readfile(curbuf->b_ffname, NULL, lnum,
1587 pp->pb_pointer[idx].pe_old_lnum - 1,
Bram Moolenaare13b9af2017-01-13 22:01:02 +01001588 line_count, NULL, 0) != OK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001589 cannot_open = TRUE;
1590 else
1591 lnum += line_count;
1592 }
1593 if (cannot_open)
1594 {
1595 ++error;
1596 ml_append(lnum++, (char_u *)_("???LINES MISSING"),
1597 (colnr_T)0, TRUE);
1598 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001599 ++idx; // get same block again for next index
Bram Moolenaar071d4272004-06-13 20:20:40 +00001600 continue;
1601 }
1602
1603 /*
1604 * going one block deeper in the tree
1605 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001606 if ((top = ml_add_stack(buf)) < 0) // new entry in stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00001607 {
1608 ++error;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001609 break; // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00001610 }
1611 ip = &(buf->b_ml.ml_stack[top]);
1612 ip->ip_bnum = bnum;
1613 ip->ip_index = idx;
1614
1615 bnum = pp->pb_pointer[idx].pe_bnum;
1616 line_count = pp->pb_pointer[idx].pe_line_count;
1617 page_count = pp->pb_pointer[idx].pe_page_count;
Bram Moolenaar986a0032011-06-13 01:07:27 +02001618 idx = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001619 continue;
1620 }
1621 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001622 else // not a pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001623 {
1624 dp = (DATA_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001625 if (dp->db_id != DATA_ID) // block id wrong
Bram Moolenaar071d4272004-06-13 20:20:40 +00001626 {
1627 if (bnum == 1)
1628 {
Bram Moolenaareaaac012022-01-02 17:00:40 +00001629 semsg(_(e_block_one_id_wrong_str_not_swp_file),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001630 mfp->mf_fname);
1631 goto theend;
1632 }
1633 ++error;
1634 ml_append(lnum++, (char_u *)_("???BLOCK MISSING"),
1635 (colnr_T)0, TRUE);
1636 }
1637 else
1638 {
1639 /*
1640 * it is a data block
1641 * Append all the lines in this block
1642 */
1643 has_error = FALSE;
1644 /*
1645 * check length of block
1646 * if wrong, use length in pointer block
1647 */
1648 if (page_count * mfp->mf_page_size != dp->db_txt_end)
1649 {
1650 ml_append(lnum++, (char_u *)_("??? from here until ???END lines may be messed up"),
1651 (colnr_T)0, TRUE);
1652 ++error;
1653 has_error = TRUE;
1654 dp->db_txt_end = page_count * mfp->mf_page_size;
1655 }
1656
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001657 // make sure there is a NUL at the end of the block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001658 *((char_u *)dp + dp->db_txt_end - 1) = NUL;
1659
1660 /*
1661 * check number of lines in block
1662 * if wrong, use count in data block
1663 */
1664 if (line_count != dp->db_line_count)
1665 {
1666 ml_append(lnum++, (char_u *)_("??? from here until ???END lines may have been inserted/deleted"),
1667 (colnr_T)0, TRUE);
1668 ++error;
1669 has_error = TRUE;
1670 }
1671
1672 for (i = 0; i < dp->db_line_count; ++i)
1673 {
1674 txt_start = (dp->db_index[i] & DB_INDEX_MASK);
Bram Moolenaar740885b2009-11-03 14:33:17 +00001675 if (txt_start <= (int)HEADER_SIZE
Bram Moolenaar071d4272004-06-13 20:20:40 +00001676 || txt_start >= (int)dp->db_txt_end)
1677 {
1678 p = (char_u *)"???";
1679 ++error;
1680 }
1681 else
1682 p = (char_u *)dp + txt_start;
1683 ml_append(lnum++, p, (colnr_T)0, TRUE);
1684 }
1685 if (has_error)
Bram Moolenaar740885b2009-11-03 14:33:17 +00001686 ml_append(lnum++, (char_u *)_("???END"),
1687 (colnr_T)0, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001688 }
1689 }
1690 }
1691
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001692 if (buf->b_ml.ml_stack_top == 0) // finished
Bram Moolenaar071d4272004-06-13 20:20:40 +00001693 break;
1694
1695 /*
1696 * go one block up in the tree
1697 */
1698 ip = &(buf->b_ml.ml_stack[--(buf->b_ml.ml_stack_top)]);
1699 bnum = ip->ip_bnum;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001700 idx = ip->ip_index + 1; // go to next index
Bram Moolenaar071d4272004-06-13 20:20:40 +00001701 page_count = 1;
1702 }
1703
1704 /*
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001705 * Compare the buffer contents with the original file. When they differ
1706 * set the 'modified' flag.
1707 * Lines 1 - lnum are the new contents.
1708 * Lines lnum + 1 to ml_line_count are the original contents.
1709 * Line ml_line_count + 1 in the dummy empty line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001710 */
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001711 if (orig_file_status != OK || curbuf->b_ml.ml_line_count != lnum * 2 + 1)
1712 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001713 // Recovering an empty file results in two lines and the first line is
1714 // empty. Don't set the modified flag then.
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001715 if (!(curbuf->b_ml.ml_line_count == 2 && *ml_get(1) == NUL))
1716 {
Bram Moolenaarec28d152019-05-11 18:36:34 +02001717 changed_internal();
Bram Moolenaar95c526e2017-02-25 14:59:34 +01001718 ++CHANGEDTICK(curbuf);
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001719 }
1720 }
1721 else
1722 {
1723 for (idx = 1; idx <= lnum; ++idx)
1724 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001725 // Need to copy one line, fetching the other one may flush it.
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001726 p = vim_strsave(ml_get(idx));
1727 i = STRCMP(p, ml_get(idx + lnum));
1728 vim_free(p);
1729 if (i != 0)
1730 {
Bram Moolenaarec28d152019-05-11 18:36:34 +02001731 changed_internal();
Bram Moolenaar95c526e2017-02-25 14:59:34 +01001732 ++CHANGEDTICK(curbuf);
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001733 break;
1734 }
1735 }
1736 }
1737
1738 /*
1739 * Delete the lines from the original file and the dummy line from the
1740 * empty buffer. These will now be after the last line in the buffer.
1741 */
1742 while (curbuf->b_ml.ml_line_count > lnum
1743 && !(curbuf->b_ml.ml_flags & ML_EMPTY))
Bram Moolenaarca70c072020-05-30 20:30:46 +02001744 ml_delete(curbuf->b_ml.ml_line_count);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001745 curbuf->b_flags |= BF_RECOVERED;
Bram Moolenaare3f50ad2021-06-09 12:33:40 +02001746 check_cursor();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001747
1748 recoverymode = FALSE;
1749 if (got_int)
Bram Moolenaareaaac012022-01-02 17:00:40 +00001750 emsg(_(e_recovery_interrupted));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001751 else if (error)
1752 {
1753 ++no_wait_return;
Bram Moolenaar32526b32019-01-19 17:43:09 +01001754 msg(">>>>>>>>>>>>>");
Bram Moolenaareaaac012022-01-02 17:00:40 +00001755 emsg(_(e_errors_detected_while_recovering_look_for_lines_starting_with_questions));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001756 --no_wait_return;
Bram Moolenaar32526b32019-01-19 17:43:09 +01001757 msg(_("See \":help E312\" for more information."));
1758 msg(">>>>>>>>>>>>>");
Bram Moolenaar071d4272004-06-13 20:20:40 +00001759 }
1760 else
1761 {
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001762 if (curbuf->b_changed)
1763 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001764 msg(_("Recovery completed. You should check if everything is OK."));
1765 msg_puts(_("\n(You might want to write out this file under another name\n"));
1766 msg_puts(_("and run diff with the original file to check for changes)"));
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001767 }
1768 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001769 msg(_("Recovery completed. Buffer contents equals file contents."));
Bram Moolenaarf8835082020-11-09 21:04:17 +01001770 msg_puts(_("\nYou may want to delete the .swp file now."));
1771#if defined(UNIX) || defined(MSWIN)
Bram Moolenaarf52f0602021-03-10 21:26:37 +01001772 if (swapfile_process_running(b0p, fname_used))
Bram Moolenaarf8835082020-11-09 21:04:17 +01001773 {
1774 // Warn there could be an active Vim on the same file, the user may
1775 // want to kill it.
1776 msg_puts(_("\nNote: process STILL RUNNING: "));
1777 msg_outnum(char_to_long(b0p->b0_pid));
1778 }
1779#endif
1780 msg_puts("\n\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00001781 cmdline_row = msg_row;
1782 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001783#ifdef FEAT_CRYPT
1784 if (*buf->b_p_key != NUL && STRCMP(curbuf->b_p_key, buf->b_p_key) != 0)
1785 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001786 msg_puts(_("Using crypt key from swap file for the text file.\n"));
Bram Moolenaar31e5c602022-04-15 13:53:33 +01001787 set_option_value_give_err((char_u *)"key", 0L, buf->b_p_key, OPT_LOCAL);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001788 }
1789#endif
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001790 redraw_curbuf_later(UPD_NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001791
1792theend:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001793 vim_free(fname_used);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001794 recoverymode = FALSE;
1795 if (mfp != NULL)
1796 {
1797 if (hp != NULL)
1798 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001799 mf_close(mfp, FALSE); // will also vim_free(mfp->mf_fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001800 }
Bram Moolenaardf88dda2007-01-09 13:34:50 +00001801 if (buf != NULL)
1802 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001803#ifdef FEAT_CRYPT
1804 if (buf->b_p_key != curbuf->b_p_key)
1805 free_string_option(buf->b_p_key);
Bram Moolenaar0ad014c2010-07-25 14:00:46 +02001806 free_string_option(buf->b_p_cm);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001807#endif
Bram Moolenaardf88dda2007-01-09 13:34:50 +00001808 vim_free(buf->b_ml.ml_stack);
1809 vim_free(buf);
1810 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001811 if (serious_error && called_from_main)
1812 ml_close(curbuf, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001813 else
1814 {
1815 apply_autocmds(EVENT_BUFREADPOST, NULL, curbuf->b_fname, FALSE, curbuf);
1816 apply_autocmds(EVENT_BUFWINENTER, NULL, curbuf->b_fname, FALSE, curbuf);
1817 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001818}
1819
1820/*
1821 * Find the names of swap files in current directory and the directory given
1822 * with the 'directory' option.
1823 *
1824 * Used to:
1825 * - list the swap files for "vim -r"
1826 * - count the number of swap files when recovering
1827 * - list the swap files when recovering
Bram Moolenaarc216a7a2022-12-05 13:50:55 +00001828 * - list the swap files for swapfilelist()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001829 * - find the name of the n'th swap file when recovering
1830 */
1831 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001832recover_names(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001833 char_u *fname, // base for swap file name
Bram Moolenaarc216a7a2022-12-05 13:50:55 +00001834 int do_list, // when TRUE, list the swap file names
1835 list_T *ret_list UNUSED, // when not NULL add file names to it
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001836 int nr, // when non-zero, return nr'th swap file name
1837 char_u **fname_out) // result when "nr" > 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001838{
1839 int num_names;
1840 char_u *(names[6]);
1841 char_u *tail;
1842 char_u *p;
1843 int num_files;
1844 int file_count = 0;
1845 char_u **files;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001846 char_u *dirp;
1847 char_u *dir_name;
Bram Moolenaar64354da2010-05-25 21:37:17 +02001848 char_u *fname_res = NULL;
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001849#ifdef HAVE_READLINK
1850 char_u fname_buf[MAXPATHL];
Bram Moolenaar64354da2010-05-25 21:37:17 +02001851#endif
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001852
Bram Moolenaar64354da2010-05-25 21:37:17 +02001853 if (fname != NULL)
1854 {
1855#ifdef HAVE_READLINK
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001856 // Expand symlink in the file name, because the swap file is created
1857 // with the actual file instead of with the symlink.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001858 if (resolve_symlink(fname, fname_buf) == OK)
1859 fname_res = fname_buf;
1860 else
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001861#endif
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001862 fname_res = fname;
Bram Moolenaar64354da2010-05-25 21:37:17 +02001863 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001864
Bram Moolenaarc216a7a2022-12-05 13:50:55 +00001865 if (do_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001866 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001867 // use msg() to start the scrolling properly
Bram Moolenaar32526b32019-01-19 17:43:09 +01001868 msg(_("Swap files found:"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001869 msg_putchar('\n');
1870 }
1871
1872 /*
1873 * Do the loop for every directory in 'directory'.
1874 * First allocate some memory to put the directory name in.
1875 */
Bram Moolenaar964b3742019-05-24 18:54:09 +02001876 dir_name = alloc(STRLEN(p_dir) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001877 dirp = p_dir;
1878 while (dir_name != NULL && *dirp)
1879 {
1880 /*
1881 * Isolate a directory name from *dirp and put it in dir_name (we know
1882 * it is large enough, so use 31000 for length).
1883 * Advance dirp to next directory name.
1884 */
1885 (void)copy_option_part(&dirp, dir_name, 31000, ",");
1886
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001887 if (dir_name[0] == '.' && dir_name[1] == NUL) // check current dir
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] = vim_strsave((char_u *)"*_sw%");
1893#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001894 names[0] = vim_strsave((char_u *)"*.sw?");
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] = vim_strsave((char_u *)".*.sw?");
1900 names[2] = vim_strsave((char_u *)".sw?");
1901 num_names = 3;
1902#else
1903# ifdef VMS
1904 names[1] = vim_strsave((char_u *)".*_sw%");
1905 num_names = 2;
1906# else
1907 num_names = 1;
1908# endif
1909#endif
1910 }
1911 else
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001912 num_names = recov_file_names(names, fname_res, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001913 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001914 else // check directory dir_name
Bram Moolenaar071d4272004-06-13 20:20:40 +00001915 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001916 if (fname == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001917 {
1918#ifdef VMS
1919 names[0] = concat_fnames(dir_name, (char_u *)"*_sw%", TRUE);
1920#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001921 names[0] = concat_fnames(dir_name, (char_u *)"*.sw?", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001922#endif
Bram Moolenaar4f974752019-02-17 17:44:42 +01001923#if defined(UNIX) || defined(MSWIN)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001924 // For Unix names starting with a dot are special. MS-Windows
1925 // supports this too, on some file systems.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001926 names[1] = concat_fnames(dir_name, (char_u *)".*.sw?", TRUE);
1927 names[2] = concat_fnames(dir_name, (char_u *)".sw?", TRUE);
1928 num_names = 3;
1929#else
1930# ifdef VMS
1931 names[1] = concat_fnames(dir_name, (char_u *)".*_sw%", TRUE);
1932 num_names = 2;
1933# else
1934 num_names = 1;
1935# endif
1936#endif
1937 }
1938 else
1939 {
Bram Moolenaar4f974752019-02-17 17:44:42 +01001940#if defined(UNIX) || defined(MSWIN)
Bram Moolenaarb113c3a2017-02-28 21:26:17 +01001941 int len = (int)STRLEN(dir_name);
Bram Moolenaarc525e3a2017-02-18 16:59:02 +01001942
1943 p = dir_name + len;
1944 if (after_pathsep(dir_name, p) && len > 1 && p[-1] == p[-2])
Bram Moolenaar071d4272004-06-13 20:20:40 +00001945 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001946 // Ends with '//', Use Full path for swap name
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001947 tail = make_percent_swname(dir_name, fname_res);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001948 }
1949 else
1950#endif
1951 {
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001952 tail = gettail(fname_res);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001953 tail = concat_fnames(dir_name, tail, TRUE);
1954 }
1955 if (tail == NULL)
1956 num_names = 0;
1957 else
1958 {
1959 num_names = recov_file_names(names, tail, FALSE);
1960 vim_free(tail);
1961 }
1962 }
1963 }
1964
Bram Moolenaar0d3cb732019-05-18 13:05:18 +02001965 // check for out-of-memory
Bram Moolenaarc216a7a2022-12-05 13:50:55 +00001966 for (int i = 0; i < num_names; ++i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001967 {
1968 if (names[i] == NULL)
1969 {
1970 for (i = 0; i < num_names; ++i)
1971 vim_free(names[i]);
1972 num_names = 0;
1973 }
1974 }
1975 if (num_names == 0)
1976 num_files = 0;
1977 else if (expand_wildcards(num_names, names, &num_files, &files,
Bram Moolenaar99499b12019-05-23 21:35:48 +02001978 EW_NOTENV|EW_KEEPALL|EW_FILE|EW_SILENT) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001979 num_files = 0;
1980
1981 /*
1982 * When no swap file found, wildcard expansion might have failed (e.g.
1983 * not able to execute the shell).
1984 * Try finding a swap file by simply adding ".swp" to the file name.
1985 */
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001986 if (*dirp == NUL && file_count + num_files == 0 && fname != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001987 {
Bram Moolenaar8767f522016-07-01 17:17:39 +02001988 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001989 char_u *swapname;
1990
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001991 swapname = modname(fname_res,
Bram Moolenaare60acc12011-05-10 16:41:25 +02001992#if defined(VMS)
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001993 (char_u *)"_swp", FALSE
Bram Moolenaar071d4272004-06-13 20:20:40 +00001994#else
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001995 (char_u *)".swp", TRUE
Bram Moolenaar071d4272004-06-13 20:20:40 +00001996#endif
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001997 );
Bram Moolenaar071d4272004-06-13 20:20:40 +00001998 if (swapname != NULL)
1999 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02002000 if (mch_stat((char *)swapname, &st) != -1) // It exists!
Bram Moolenaar071d4272004-06-13 20:20:40 +00002001 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02002002 files = ALLOC_ONE(char_u *);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002003 if (files != NULL)
2004 {
2005 files[0] = swapname;
2006 swapname = NULL;
2007 num_files = 1;
2008 }
2009 }
2010 vim_free(swapname);
2011 }
2012 }
2013
2014 /*
Bram Moolenaarc216a7a2022-12-05 13:50:55 +00002015 * Remove swapfile name of the current buffer, it must be ignored.
2016 * But keep it for swapfilelist().
Bram Moolenaar071d4272004-06-13 20:20:40 +00002017 */
2018 if (curbuf->b_ml.ml_mfp != NULL
Bram Moolenaarc216a7a2022-12-05 13:50:55 +00002019 && (p = curbuf->b_ml.ml_mfp->mf_fname) != NULL
2020 && ret_list == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002021 {
Bram Moolenaarc216a7a2022-12-05 13:50:55 +00002022 for (int i = 0; i < num_files; ++i)
Bram Moolenaar99499b12019-05-23 21:35:48 +02002023 // Do not expand wildcards, on windows would try to expand
2024 // "%tmp%" in "%tmp%file".
2025 if (fullpathcmp(p, files[i], TRUE, FALSE) & FPC_SAME)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002026 {
Bram Moolenaar99499b12019-05-23 21:35:48 +02002027 // Remove the name from files[i]. Move further entries
2028 // down. When the array becomes empty free it here, since
2029 // FreeWild() won't be called below.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002030 vim_free(files[i]);
Bram Moolenaar9439cdd2009-04-22 13:39:36 +00002031 if (--num_files == 0)
2032 vim_free(files);
2033 else
2034 for ( ; i < num_files; ++i)
2035 files[i] = files[i + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002036 }
2037 }
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00002038 if (nr > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002039 {
2040 file_count += num_files;
2041 if (nr <= file_count)
2042 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002043 *fname_out = vim_strsave(
2044 files[nr - 1 + num_files - file_count]);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002045 dirp = (char_u *)""; // stop searching
Bram Moolenaar071d4272004-06-13 20:20:40 +00002046 }
2047 }
Bram Moolenaarc216a7a2022-12-05 13:50:55 +00002048 else if (do_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002049 {
2050 if (dir_name[0] == '.' && dir_name[1] == NUL)
2051 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002052 if (fname == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002053 msg_puts(_(" In current directory:\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002054 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002055 msg_puts(_(" Using specified name:\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002056 }
2057 else
2058 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002059 msg_puts(_(" In directory "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002060 msg_home_replace(dir_name);
Bram Moolenaar32526b32019-01-19 17:43:09 +01002061 msg_puts(":\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00002062 }
2063
2064 if (num_files)
2065 {
Bram Moolenaarc216a7a2022-12-05 13:50:55 +00002066 for (int i = 0; i < num_files; ++i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002067 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002068 // print the swap file name
Bram Moolenaar071d4272004-06-13 20:20:40 +00002069 msg_outnum((long)++file_count);
Bram Moolenaar32526b32019-01-19 17:43:09 +01002070 msg_puts(". ");
2071 msg_puts((char *)gettail(files[i]));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002072 msg_putchar('\n');
2073 (void)swapfile_info(files[i]);
2074 }
2075 }
2076 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002077 msg_puts(_(" -- none --\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002078 out_flush();
2079 }
Bram Moolenaarc216a7a2022-12-05 13:50:55 +00002080#ifdef FEAT_EVAL
2081 else if (ret_list != NULL)
2082 {
2083 for (int i = 0; i < num_files; ++i)
2084 {
2085 char_u *name = concat_fnames(dir_name, files[i], TRUE);
2086 if (name != NULL)
2087 {
2088 list_append_string(ret_list, name, -1);
2089 vim_free(name);
2090 }
2091 }
2092 }
2093#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002094 else
2095 file_count += num_files;
2096
Bram Moolenaarc216a7a2022-12-05 13:50:55 +00002097 for (int i = 0; i < num_names; ++i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002098 vim_free(names[i]);
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00002099 if (num_files > 0)
2100 FreeWild(num_files, files);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002101 }
2102 vim_free(dir_name);
2103 return file_count;
2104}
2105
Bram Moolenaar4f974752019-02-17 17:44:42 +01002106#if defined(UNIX) || defined(MSWIN) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002107/*
Bram Moolenaarb782ba42018-08-07 21:39:28 +02002108 * Need _very_ long file names.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002109 * Append the full path to name with path separators made into percent
Bram Moolenaar8b0e62c2021-10-19 22:12:25 +01002110 * signs, to "dir". An unnamed buffer is handled as "" (<currentdir>/"")
2111 * The last character in "dir" must be an extra slash or backslash, it is
2112 * removed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002113 */
Bram Moolenaarb782ba42018-08-07 21:39:28 +02002114 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002115make_percent_swname(char_u *dir, char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002116{
Bram Moolenaarb782ba42018-08-07 21:39:28 +02002117 char_u *d = NULL, *s, *f;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002118
Bram Moolenaarb782ba42018-08-07 21:39:28 +02002119 f = fix_fname(name != NULL ? name : (char_u *)"");
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002120 if (f == NULL)
2121 return NULL;
Bram Moolenaar8b0e62c2021-10-19 22:12:25 +01002122
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002123 s = alloc(STRLEN(f) + 1);
2124 if (s != NULL)
2125 {
2126 STRCPY(s, f);
2127 for (d = s; *d != NUL; MB_PTR_ADV(d))
2128 if (vim_ispathsep(*d))
2129 *d = '%';
2130
2131 dir[STRLEN(dir) - 1] = NUL; // remove one trailing slash
2132 d = concat_fnames(dir, s, TRUE);
2133 vim_free(s);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002134 }
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002135 vim_free(f);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002136 return d;
2137}
2138#endif
2139
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02002140#if (defined(UNIX) || defined(VMS) || defined(MSWIN)) \
2141 && (defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG))
2142# define HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar071d4272004-06-13 20:20:40 +00002143static int process_still_running;
2144#endif
2145
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002146#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002147/*
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002148 * Return information found in swapfile "fname" in dictionary "d".
2149 * This is used by the swapinfo() function.
2150 */
2151 void
2152get_b0_dict(char_u *fname, dict_T *d)
2153{
2154 int fd;
2155 struct block0 b0;
2156
2157 if ((fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0)) >= 0)
2158 {
2159 if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0))
2160 {
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002161 if (ml_check_b0_id(&b0) == FAIL)
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002162 dict_add_string(d, "error", (char_u *)"Not a swap file");
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002163 else if (b0_magic_wrong(&b0))
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002164 dict_add_string(d, "error", (char_u *)"Magic number mismatch");
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002165 else
2166 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002167 // we have swap information
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002168 dict_add_string_len(d, "version", b0.b0_version, 10);
2169 dict_add_string_len(d, "user", b0.b0_uname, B0_UNAME_SIZE);
2170 dict_add_string_len(d, "host", b0.b0_hname, B0_HNAME_SIZE);
2171 dict_add_string_len(d, "fname", b0.b0_fname, B0_FNAME_SIZE_ORG);
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002172
2173 dict_add_number(d, "pid", char_to_long(b0.b0_pid));
2174 dict_add_number(d, "mtime", char_to_long(b0.b0_mtime));
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002175 dict_add_number(d, "dirty", b0.b0_dirty ? 1 : 0);
2176# ifdef CHECK_INODE
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002177 dict_add_number(d, "inode", char_to_long(b0.b0_ino));
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002178# endif
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002179 }
2180 }
2181 else
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002182 dict_add_string(d, "error", (char_u *)"Cannot read file");
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002183 close(fd);
2184 }
2185 else
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002186 dict_add_string(d, "error", (char_u *)"Cannot open file");
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002187}
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002188#endif
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002189
2190/*
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00002191 * Give information about an existing swap file.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002192 * Returns timestamp (0 when unknown).
2193 */
2194 static time_t
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002195swapfile_info(char_u *fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002196{
Bram Moolenaar8767f522016-07-01 17:17:39 +02002197 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002198 int fd;
2199 struct block0 b0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002200#ifdef UNIX
2201 char_u uname[B0_UNAME_SIZE];
2202#endif
2203
Bram Moolenaar63d25552019-05-10 21:28:38 +02002204 // print the swap file date
Bram Moolenaar071d4272004-06-13 20:20:40 +00002205 if (mch_stat((char *)fname, &st) != -1)
2206 {
2207#ifdef UNIX
Bram Moolenaar63d25552019-05-10 21:28:38 +02002208 // print name of owner of the file
Bram Moolenaar071d4272004-06-13 20:20:40 +00002209 if (mch_get_uname(st.st_uid, uname, B0_UNAME_SIZE) == OK)
2210 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002211 msg_puts(_(" owned by: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002212 msg_outtrans(uname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01002213 msg_puts(_(" dated: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002214 }
2215 else
2216#endif
Bram Moolenaar32526b32019-01-19 17:43:09 +01002217 msg_puts(_(" dated: "));
Bram Moolenaar63d25552019-05-10 21:28:38 +02002218 msg_puts(get_ctime(st.st_mtime, TRUE));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002219 }
Bram Moolenaar63d25552019-05-10 21:28:38 +02002220 else
2221 st.st_mtime = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002222
2223 /*
2224 * print the original file name
2225 */
2226 fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
2227 if (fd >= 0)
2228 {
Bram Moolenaar540fc6f2010-12-17 16:27:16 +01002229 if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002230 {
2231 if (STRNCMP(b0.b0_version, "VIM 3.0", 7) == 0)
2232 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002233 msg_puts(_(" [from Vim version 3.0]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002234 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002235 else if (ml_check_b0_id(&b0) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002236 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002237 msg_puts(_(" [does not look like a Vim swap file]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002238 }
2239 else
2240 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002241 msg_puts(_(" file name: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002242 if (b0.b0_fname[0] == NUL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002243 msg_puts(_("[No Name]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002244 else
2245 msg_outtrans(b0.b0_fname);
2246
Bram Moolenaar32526b32019-01-19 17:43:09 +01002247 msg_puts(_("\n modified: "));
2248 msg_puts(b0.b0_dirty ? _("YES") : _("no"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002249
2250 if (*(b0.b0_uname) != NUL)
2251 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002252 msg_puts(_("\n user name: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002253 msg_outtrans(b0.b0_uname);
2254 }
2255
2256 if (*(b0.b0_hname) != NUL)
2257 {
2258 if (*(b0.b0_uname) != NUL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002259 msg_puts(_(" host name: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002260 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002261 msg_puts(_("\n host name: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002262 msg_outtrans(b0.b0_hname);
2263 }
2264
2265 if (char_to_long(b0.b0_pid) != 0L)
2266 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002267 msg_puts(_("\n process ID: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002268 msg_outnum(char_to_long(b0.b0_pid));
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002269#if defined(UNIX) || defined(MSWIN)
Bram Moolenaarf52f0602021-03-10 21:26:37 +01002270 if (swapfile_process_running(&b0, fname))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002271 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002272 msg_puts(_(" (STILL RUNNING)"));
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02002273# ifdef HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar071d4272004-06-13 20:20:40 +00002274 process_still_running = TRUE;
2275# endif
2276 }
2277#endif
2278 }
2279
2280 if (b0_magic_wrong(&b0))
2281 {
Bram Moolenaar48e330a2016-02-23 14:53:34 +01002282#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002283 if (STRNCMP(b0.b0_hname, "PC ", 3) == 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002284 msg_puts(_("\n [not usable with this version of Vim]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002285 else
2286#endif
Bram Moolenaar32526b32019-01-19 17:43:09 +01002287 msg_puts(_("\n [not usable on this computer]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002288 }
2289 }
2290 }
2291 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002292 msg_puts(_(" [cannot be read]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002293 close(fd);
2294 }
2295 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002296 msg_puts(_(" [cannot be opened]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002297 msg_putchar('\n');
2298
Bram Moolenaar63d25552019-05-10 21:28:38 +02002299 return st.st_mtime;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002300}
2301
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002302/*
2303 * Return TRUE if the swap file looks OK and there are no changes, thus it can
2304 * be safely deleted.
2305 */
zeertzjq3c5999e2022-03-23 13:54:51 +00002306 static int
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002307swapfile_unchanged(char_u *fname)
2308{
2309 stat_T st;
2310 int fd;
2311 struct block0 b0;
2312 int ret = TRUE;
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002313
2314 // must be able to stat the swap file
2315 if (mch_stat((char *)fname, &st) == -1)
2316 return FALSE;
2317
2318 // must be able to read the first block
2319 fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
2320 if (fd < 0)
2321 return FALSE;
2322 if (read_eintr(fd, &b0, sizeof(b0)) != sizeof(b0))
2323 {
2324 close(fd);
2325 return FALSE;
2326 }
2327
2328 // the ID and magic number must be correct
2329 if (ml_check_b0_id(&b0) == FAIL|| b0_magic_wrong(&b0))
2330 ret = FALSE;
2331
2332 // must be unchanged
2333 if (b0.b0_dirty)
2334 ret = FALSE;
2335
2336#if defined(UNIX) || defined(MSWIN)
Bram Moolenaarf8835082020-11-09 21:04:17 +01002337 // Host name must be known and must equal the current host name, otherwise
2338 // comparing pid is meaningless.
2339 if (*(b0.b0_hname) == NUL)
2340 {
2341 ret = FALSE;
2342 }
2343 else
2344 {
2345 char_u hostname[B0_HNAME_SIZE];
2346
2347 mch_get_host_name(hostname, B0_HNAME_SIZE);
2348 hostname[B0_HNAME_SIZE - 1] = NUL;
Bram Moolenaare79cdb62020-11-21 13:51:16 +01002349 b0.b0_hname[B0_HNAME_SIZE - 1] = NUL; // in case of corruption
Bram Moolenaarf8835082020-11-09 21:04:17 +01002350 if (STRICMP(b0.b0_hname, hostname) != 0)
2351 ret = FALSE;
2352 }
2353
Bram Moolenaar32aa1022019-11-02 22:54:41 +01002354 // process must be known and not be running
Bram Moolenaarf52f0602021-03-10 21:26:37 +01002355 if (char_to_long(b0.b0_pid) == 0L || swapfile_process_running(&b0, fname))
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002356 ret = FALSE;
2357#endif
2358
Bram Moolenaarf8835082020-11-09 21:04:17 +01002359 // We do not check the user, it should be irrelevant for whether the swap
2360 // file is still useful.
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002361
2362 close(fd);
2363 return ret;
2364}
2365
Bram Moolenaar071d4272004-06-13 20:20:40 +00002366 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002367recov_file_names(char_u **names, char_u *path, int prepend_dot)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002368{
2369 int num_names;
2370
Bram Moolenaar071d4272004-06-13 20:20:40 +00002371 /*
2372 * (Win32 and Win64) never short names, but do prepend a dot.
2373 * (Not MS-DOS or Win32 or Win64) maybe short name, maybe not: Try both.
2374 * Only use the short name if it is different.
2375 */
2376 char_u *p;
2377 int i;
Bram Moolenaar4f974752019-02-17 17:44:42 +01002378# ifndef MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002379 int shortname = curbuf->b_shortname;
2380
2381 curbuf->b_shortname = FALSE;
2382# endif
2383
2384 num_names = 0;
2385
2386 /*
2387 * May also add the file name with a dot prepended, for swap file in same
2388 * dir as original file.
2389 */
2390 if (prepend_dot)
2391 {
2392 names[num_names] = modname(path, (char_u *)".sw?", TRUE);
2393 if (names[num_names] == NULL)
2394 goto end;
2395 ++num_names;
2396 }
2397
2398 /*
2399 * Form the normal swap file name pattern by appending ".sw?".
2400 */
2401#ifdef VMS
2402 names[num_names] = concat_fnames(path, (char_u *)"_sw%", FALSE);
2403#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002404 names[num_names] = concat_fnames(path, (char_u *)".sw?", FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002405#endif
2406 if (names[num_names] == NULL)
2407 goto end;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002408 if (num_names >= 1) // check if we have the same name twice
Bram Moolenaar071d4272004-06-13 20:20:40 +00002409 {
2410 p = names[num_names - 1];
2411 i = (int)STRLEN(names[num_names - 1]) - (int)STRLEN(names[num_names]);
2412 if (i > 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002413 p += i; // file name has been expanded to full path
Bram Moolenaar071d4272004-06-13 20:20:40 +00002414
2415 if (STRCMP(p, names[num_names]) != 0)
2416 ++num_names;
2417 else
2418 vim_free(names[num_names]);
2419 }
2420 else
2421 ++num_names;
2422
Bram Moolenaar4f974752019-02-17 17:44:42 +01002423# ifndef MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002424 /*
2425 * Also try with 'shortname' set, in case the file is on a DOS filesystem.
2426 */
2427 curbuf->b_shortname = TRUE;
2428#ifdef VMS
2429 names[num_names] = modname(path, (char_u *)"_sw%", FALSE);
2430#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002431 names[num_names] = modname(path, (char_u *)".sw?", FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002432#endif
2433 if (names[num_names] == NULL)
2434 goto end;
2435
2436 /*
2437 * Remove the one from 'shortname', if it's the same as with 'noshortname'.
2438 */
2439 p = names[num_names];
2440 i = STRLEN(names[num_names]) - STRLEN(names[num_names - 1]);
2441 if (i > 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002442 p += i; // file name has been expanded to full path
Bram Moolenaar071d4272004-06-13 20:20:40 +00002443 if (STRCMP(names[num_names - 1], p) == 0)
2444 vim_free(names[num_names]);
2445 else
2446 ++num_names;
2447# endif
2448
2449end:
Bram Moolenaar4f974752019-02-17 17:44:42 +01002450# ifndef MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002451 curbuf->b_shortname = shortname;
2452# endif
2453
Bram Moolenaar071d4272004-06-13 20:20:40 +00002454 return num_names;
2455}
2456
2457/*
2458 * sync all memlines
2459 *
2460 * If 'check_file' is TRUE, check if original file exists and was not changed.
2461 * If 'check_char' is TRUE, stop syncing when character becomes available, but
2462 * always sync at least one block.
2463 */
2464 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002465ml_sync_all(int check_file, int check_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002466{
2467 buf_T *buf;
Bram Moolenaar8767f522016-07-01 17:17:39 +02002468 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002469
Bram Moolenaar29323592016-07-24 22:04:11 +02002470 FOR_ALL_BUFFERS(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002471 {
Christian Brabandtf573c6e2021-06-20 14:02:16 +02002472 if (buf->b_ml.ml_mfp == NULL
2473 || buf->b_ml.ml_mfp->mf_fname == NULL
2474 || buf->b_ml.ml_mfp->mf_fd < 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002475 continue; // no file
Bram Moolenaar071d4272004-06-13 20:20:40 +00002476
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002477 ml_flush_line(buf); // flush buffered line
2478 // flush locked block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002479 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH);
2480 if (bufIsChanged(buf) && check_file && mf_need_trans(buf->b_ml.ml_mfp)
2481 && buf->b_ffname != NULL)
2482 {
2483 /*
2484 * If the original file does not exist anymore or has been changed
2485 * call ml_preserve() to get rid of all negative numbered blocks.
2486 */
2487 if (mch_stat((char *)buf->b_ffname, &st) == -1
2488 || st.st_mtime != buf->b_mtime_read
Leah Neukirchen0a7984a2021-10-14 21:27:55 +01002489#ifdef ST_MTIM_NSEC
2490 || st.ST_MTIM_NSEC != buf->b_mtime_read_ns
2491#endif
Bram Moolenaar914703b2010-05-31 21:59:46 +02002492 || st.st_size != buf->b_orig_size)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002493 {
2494 ml_preserve(buf, FALSE);
2495 did_check_timestamps = FALSE;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002496 need_check_timestamps = TRUE; // give message later
Bram Moolenaar071d4272004-06-13 20:20:40 +00002497 }
2498 }
2499 if (buf->b_ml.ml_mfp->mf_dirty)
2500 {
2501 (void)mf_sync(buf->b_ml.ml_mfp, (check_char ? MFS_STOP : 0)
2502 | (bufIsChanged(buf) ? MFS_FLUSH : 0));
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002503 if (check_char && ui_char_avail()) // character available now
Bram Moolenaar071d4272004-06-13 20:20:40 +00002504 break;
2505 }
2506 }
2507}
2508
2509/*
2510 * sync one buffer, including negative blocks
2511 *
2512 * after this all the blocks are in the swap file
2513 *
2514 * Used for the :preserve command and when the original file has been
2515 * changed or deleted.
2516 *
2517 * when message is TRUE the success of preserving is reported
2518 */
2519 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002520ml_preserve(buf_T *buf, int message)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002521{
2522 bhdr_T *hp;
2523 linenr_T lnum;
2524 memfile_T *mfp = buf->b_ml.ml_mfp;
2525 int status;
2526 int got_int_save = got_int;
2527
2528 if (mfp == NULL || mfp->mf_fname == NULL)
2529 {
2530 if (message)
Bram Moolenaareaaac012022-01-02 17:00:40 +00002531 emsg(_(e_cannot_preserve_there_is_no_swap_file));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002532 return;
2533 }
2534
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002535 // We only want to stop when interrupted here, not when interrupted
2536 // before.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002537 got_int = FALSE;
2538
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002539 ml_flush_line(buf); // flush buffered line
2540 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush locked block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002541 status = mf_sync(mfp, MFS_ALL | MFS_FLUSH);
2542
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002543 // stack is invalid after mf_sync(.., MFS_ALL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002544 buf->b_ml.ml_stack_top = 0;
2545
2546 /*
2547 * Some of the data blocks may have been changed from negative to
2548 * positive block number. In that case the pointer blocks need to be
2549 * updated.
2550 *
2551 * We don't know in which pointer block the references are, so we visit
2552 * all data blocks until there are no more translations to be done (or
2553 * we hit the end of the file, which can only happen in case a write fails,
2554 * e.g. when file system if full).
2555 * ml_find_line() does the work by translating the negative block numbers
2556 * when getting the first line of each data block.
2557 */
2558 if (mf_need_trans(mfp) && !got_int)
2559 {
2560 lnum = 1;
2561 while (mf_need_trans(mfp) && lnum <= buf->b_ml.ml_line_count)
2562 {
2563 hp = ml_find_line(buf, lnum, ML_FIND);
2564 if (hp == NULL)
2565 {
2566 status = FAIL;
2567 goto theend;
2568 }
2569 CHECK(buf->b_ml.ml_locked_low != lnum, "low != lnum");
2570 lnum = buf->b_ml.ml_locked_high + 1;
2571 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002572 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush locked block
2573 // sync the updated pointer blocks
Bram Moolenaar071d4272004-06-13 20:20:40 +00002574 if (mf_sync(mfp, MFS_ALL | MFS_FLUSH) == FAIL)
2575 status = FAIL;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002576 buf->b_ml.ml_stack_top = 0; // stack is invalid now
Bram Moolenaar071d4272004-06-13 20:20:40 +00002577 }
2578theend:
2579 got_int |= got_int_save;
2580
2581 if (message)
2582 {
2583 if (status == OK)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002584 msg(_("File preserved"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002585 else
Bram Moolenaareaaac012022-01-02 17:00:40 +00002586 emsg(_(e_preserve_failed));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002587 }
2588}
2589
2590/*
2591 * NOTE: The pointer returned by the ml_get_*() functions only remains valid
2592 * until the next call!
2593 * line1 = ml_get(1);
2594 * line2 = ml_get(2); // line1 is now invalid!
2595 * Make a copy of the line if necessary.
2596 */
2597/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002598 * Return a pointer to a (read-only copy of a) line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002599 *
2600 * On failure an error message is given and IObuff is returned (to avoid
2601 * having to check for error everywhere).
2602 */
2603 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002604ml_get(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002605{
2606 return ml_get_buf(curbuf, lnum, FALSE);
2607}
2608
2609/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002610 * Return pointer to position "pos".
Bram Moolenaar071d4272004-06-13 20:20:40 +00002611 */
2612 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002613ml_get_pos(pos_T *pos)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002614{
2615 return (ml_get_buf(curbuf, pos->lnum, FALSE) + pos->col);
2616}
2617
2618/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002619 * Return pointer to cursor line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002620 */
2621 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002622ml_get_curline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002623{
2624 return ml_get_buf(curbuf, curwin->w_cursor.lnum, FALSE);
2625}
2626
2627/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002628 * Return pointer to cursor position.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002629 */
2630 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002631ml_get_cursor(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002632{
2633 return (ml_get_buf(curbuf, curwin->w_cursor.lnum, FALSE) +
2634 curwin->w_cursor.col);
2635}
2636
2637/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002638 * Return a pointer to a line in a specific buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +00002639 *
2640 * "will_change": if TRUE mark the buffer dirty (chars in the line will be
2641 * changed)
2642 */
2643 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002644ml_get_buf(
2645 buf_T *buf,
2646 linenr_T lnum,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002647 int will_change) // line will be changed
Bram Moolenaar071d4272004-06-13 20:20:40 +00002648{
Bram Moolenaarad40f022007-02-13 03:01:39 +00002649 bhdr_T *hp;
2650 DATA_BL *dp;
Bram Moolenaarad40f022007-02-13 03:01:39 +00002651 static int recursive = 0;
Bram Moolenaar96e7a592021-11-25 13:52:37 +00002652 static char_u questions[4];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002653
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002654 if (lnum > buf->b_ml.ml_line_count) // invalid line number
Bram Moolenaar071d4272004-06-13 20:20:40 +00002655 {
Bram Moolenaarad40f022007-02-13 03:01:39 +00002656 if (recursive == 0)
2657 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002658 // Avoid giving this message for a recursive call, may happen when
2659 // the GUI redraws part of the text.
Bram Moolenaarad40f022007-02-13 03:01:39 +00002660 ++recursive;
Bram Moolenaareaaac012022-01-02 17:00:40 +00002661 siemsg(_(e_ml_get_invalid_lnum_nr), lnum);
Bram Moolenaarad40f022007-02-13 03:01:39 +00002662 --recursive;
2663 }
Bram Moolenaarf9435e42022-02-16 16:33:28 +00002664 ml_flush_line(buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002665errorret:
Bram Moolenaar96e7a592021-11-25 13:52:37 +00002666 STRCPY(questions, "???");
Bram Moolenaaradfde112019-05-25 22:11:45 +02002667 buf->b_ml.ml_line_len = 4;
Bram Moolenaarf9435e42022-02-16 16:33:28 +00002668 buf->b_ml.ml_line_lnum = lnum;
Bram Moolenaar96e7a592021-11-25 13:52:37 +00002669 return questions;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002670 }
Bram Moolenaaradfde112019-05-25 22:11:45 +02002671 if (lnum <= 0) // pretend line 0 is line 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00002672 lnum = 1;
2673
Bram Moolenaaradfde112019-05-25 22:11:45 +02002674 if (buf->b_ml.ml_mfp == NULL) // there are no lines
2675 {
2676 buf->b_ml.ml_line_len = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002677 return (char_u *)"";
Bram Moolenaaradfde112019-05-25 22:11:45 +02002678 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002679
Bram Moolenaar37d619f2010-03-10 14:46:26 +01002680 /*
2681 * See if it is the same line as requested last time.
2682 * Otherwise may need to flush last used line.
2683 * Don't use the last used line when 'swapfile' is reset, need to load all
2684 * blocks.
2685 */
Bram Moolenaar47b8b152007-02-07 02:41:57 +00002686 if (buf->b_ml.ml_line_lnum != lnum || mf_dont_release)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002687 {
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002688 unsigned start, end;
2689 colnr_T len;
2690 int idx;
2691
Bram Moolenaar071d4272004-06-13 20:20:40 +00002692 ml_flush_line(buf);
2693
2694 /*
2695 * Find the data block containing the line.
2696 * This also fills the stack with the blocks from the root to the data
2697 * block and releases any locked block.
2698 */
2699 if ((hp = ml_find_line(buf, lnum, ML_FIND)) == NULL)
2700 {
Bram Moolenaarad40f022007-02-13 03:01:39 +00002701 if (recursive == 0)
2702 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002703 // Avoid giving this message for a recursive call, may happen
2704 // when the GUI redraws part of the text.
Bram Moolenaarad40f022007-02-13 03:01:39 +00002705 ++recursive;
Bram Moolenaarcb868932019-10-26 20:56:21 +02002706 get_trans_bufname(buf);
2707 shorten_dir(NameBuff);
Bram Moolenaareaaac012022-01-02 17:00:40 +00002708 siemsg(_(e_ml_get_cannot_find_line_nr_in_buffer_nr_str),
Bram Moolenaarcb868932019-10-26 20:56:21 +02002709 lnum, buf->b_fnum, NameBuff);
Bram Moolenaarad40f022007-02-13 03:01:39 +00002710 --recursive;
2711 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002712 goto errorret;
2713 }
2714
2715 dp = (DATA_BL *)(hp->bh_data);
2716
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002717 idx = lnum - buf->b_ml.ml_locked_low;
2718 start = ((dp->db_index[idx]) & DB_INDEX_MASK);
2719 // The text ends where the previous line starts. The first line ends
2720 // at the end of the block.
2721 if (idx == 0)
2722 end = dp->db_txt_end;
2723 else
2724 end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK);
2725 len = end - start;
2726
2727 buf->b_ml.ml_line_ptr = (char_u *)dp + start;
2728 buf->b_ml.ml_line_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002729 buf->b_ml.ml_line_lnum = lnum;
Bram Moolenaarfa4873c2022-06-30 22:13:59 +01002730 buf->b_ml.ml_flags &= ~(ML_LINE_DIRTY | ML_ALLOCATED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002731 }
2732 if (will_change)
Bram Moolenaarfa4873c2022-06-30 22:13:59 +01002733 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002734 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
Bram Moolenaarfa4873c2022-06-30 22:13:59 +01002735#ifdef FEAT_EVAL
2736 if (ml_get_alloc_lines && (buf->b_ml.ml_flags & ML_ALLOCATED))
2737 // can't make the change in the data block
2738 buf->b_ml.ml_flags |= ML_LINE_DIRTY;
2739#endif
2740 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002741
Bram Moolenaarfa4873c2022-06-30 22:13:59 +01002742#ifdef FEAT_EVAL
2743 if (ml_get_alloc_lines
2744 && (buf->b_ml.ml_flags & (ML_LINE_DIRTY | ML_ALLOCATED)) == 0)
2745 {
2746 char_u *p = alloc(buf->b_ml.ml_line_len);
2747
2748 // make sure the text is in allocated memory
2749 if (p != NULL)
2750 {
2751 memmove(p, buf->b_ml.ml_line_ptr, buf->b_ml.ml_line_len);
2752 buf->b_ml.ml_line_ptr = p;
2753 buf->b_ml.ml_flags |= ML_ALLOCATED;
2754 if (will_change)
2755 // can't make the change in the data block
2756 buf->b_ml.ml_flags |= ML_LINE_DIRTY;
2757 }
2758 }
2759#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002760 return buf->b_ml.ml_line_ptr;
2761}
2762
2763/*
2764 * Check if a line that was just obtained by a call to ml_get
2765 * is in allocated memory.
Bram Moolenaarfa4873c2022-06-30 22:13:59 +01002766 * This ignores ML_ALLOCATED to get the same behavior as without the test
2767 * override.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002768 */
2769 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002770ml_line_alloced(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002771{
2772 return (curbuf->b_ml.ml_flags & ML_LINE_DIRTY);
2773}
2774
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002775#ifdef FEAT_PROP_POPUP
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002776/*
2777 * Add text properties that continue from the previous line.
2778 */
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002779 static void
2780add_text_props_for_append(
2781 buf_T *buf,
2782 linenr_T lnum,
2783 char_u **line,
2784 int *len,
2785 char_u **tofree)
2786{
2787 int round;
2788 int new_prop_count = 0;
2789 int count;
2790 int n;
2791 char_u *props;
Bram Moolenaarea781452019-09-04 18:53:12 +02002792 int new_len = 0; // init for gcc
Bram Moolenaar8f13d822020-09-12 21:04:23 +02002793 char_u *new_line = NULL;
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002794 textprop_T prop;
2795
2796 // Make two rounds:
2797 // 1. calculate the extra space needed
2798 // 2. allocate the space and fill it
2799 for (round = 1; round <= 2; ++round)
2800 {
2801 if (round == 2)
2802 {
2803 if (new_prop_count == 0)
2804 return; // nothing to do
2805 new_len = *len + new_prop_count * sizeof(textprop_T);
Bram Moolenaar964b3742019-05-24 18:54:09 +02002806 new_line = alloc(new_len);
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002807 if (new_line == NULL)
2808 return;
2809 mch_memmove(new_line, *line, *len);
2810 new_prop_count = 0;
2811 }
2812
2813 // Get the line above to find any props that continue in the next
2814 // line.
2815 count = get_text_props(buf, lnum, &props, FALSE);
2816 for (n = 0; n < count; ++n)
2817 {
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002818 mch_memmove(&prop, props + n * sizeof(textprop_T),
2819 sizeof(textprop_T));
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002820 if (prop.tp_flags & TP_FLAG_CONT_NEXT)
2821 {
2822 if (round == 2)
2823 {
2824 prop.tp_flags |= TP_FLAG_CONT_PREV;
2825 prop.tp_col = 1;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002826 prop.tp_len = *len; // not exactly the right length
2827 mch_memmove(new_line + *len + new_prop_count
2828 * sizeof(textprop_T), &prop, sizeof(textprop_T));
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002829 }
2830 ++new_prop_count;
2831 }
2832 }
2833 }
2834 *line = new_line;
2835 *tofree = new_line;
2836 *len = new_len;
2837}
2838#endif
2839
Bram Moolenaar071d4272004-06-13 20:20:40 +00002840 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002841ml_append_int(
2842 buf_T *buf,
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002843 linenr_T lnum, // append after this line (can be 0)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002844 char_u *line_arg, // text of the new line
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002845 colnr_T len_arg, // length of line, including NUL, or 0
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002846 int flags) // ML_APPEND_ flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00002847{
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002848 char_u *line = line_arg;
2849 colnr_T len = len_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002850 int i;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002851 int line_count; // number of indexes in current block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002852 int offset;
2853 int from, to;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002854 int space_needed; // space needed for new line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002855 int page_size;
2856 int page_count;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002857 int db_idx; // index for lnum in data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002858 bhdr_T *hp;
2859 memfile_T *mfp;
2860 DATA_BL *dp;
2861 PTR_BL *pp;
2862 infoptr_T *ip;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002863#ifdef FEAT_PROP_POPUP
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002864 char_u *tofree = NULL;
Bram Moolenaar50652b02022-08-07 21:48:37 +01002865# ifdef FEAT_BYTEOFF
2866 colnr_T text_len = 0; // text len with NUL without text properties
2867# endif
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002868#endif
2869 int ret = FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002870
Bram Moolenaar071d4272004-06-13 20:20:40 +00002871 if (lnum > buf->b_ml.ml_line_count || buf->b_ml.ml_mfp == NULL)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002872 return FAIL; // lnum out of range
Bram Moolenaar071d4272004-06-13 20:20:40 +00002873
2874 if (lowest_marked && lowest_marked > lnum)
2875 lowest_marked = lnum + 1;
2876
2877 if (len == 0)
Bram Moolenaar50652b02022-08-07 21:48:37 +01002878 {
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002879 len = (colnr_T)STRLEN(line) + 1; // space needed for the text
Bram Moolenaar50652b02022-08-07 21:48:37 +01002880#if defined(FEAT_PROP_POPUP) && defined(FEAT_BYTEOFF)
2881 text_len = len;
2882#endif
2883 }
2884#if defined(FEAT_PROP_POPUP) && defined(FEAT_BYTEOFF)
2885 else if (curbuf->b_has_textprop)
2886 // "len" may include text properties, get the length of the text.
2887 text_len = (colnr_T)STRLEN(line) + 1;
2888 else
2889 text_len = len;
2890#endif
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002891
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002892#ifdef FEAT_PROP_POPUP
Bram Moolenaar840f91f2021-05-26 22:32:10 +02002893 if (curbuf->b_has_textprop && lnum > 0
2894 && !(flags & (ML_APPEND_UNDO | ML_APPEND_NOPROP)))
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002895 // Add text properties that continue from the previous line.
2896 add_text_props_for_append(buf, lnum, &line, &len, &tofree);
2897#endif
2898
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002899 space_needed = len + INDEX_SIZE; // space needed for text + index
Bram Moolenaar071d4272004-06-13 20:20:40 +00002900
2901 mfp = buf->b_ml.ml_mfp;
2902 page_size = mfp->mf_page_size;
2903
2904/*
2905 * find the data block containing the previous line
2906 * This also fills the stack with the blocks from the root to the data block
2907 * This also releases any locked block.
2908 */
2909 if ((hp = ml_find_line(buf, lnum == 0 ? (linenr_T)1 : lnum,
2910 ML_INSERT)) == NULL)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002911 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002912
2913 buf->b_ml.ml_flags &= ~ML_EMPTY;
2914
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002915 if (lnum == 0) // got line one instead, correct db_idx
2916 db_idx = -1; // careful, it is negative!
Bram Moolenaar071d4272004-06-13 20:20:40 +00002917 else
2918 db_idx = lnum - buf->b_ml.ml_locked_low;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002919 // get line count before the insertion
Bram Moolenaar071d4272004-06-13 20:20:40 +00002920 line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low;
2921
2922 dp = (DATA_BL *)(hp->bh_data);
2923
2924/*
2925 * If
2926 * - there is not enough room in the current block
2927 * - appending to the last line in the block
2928 * - not appending to the last line in the file
2929 * insert in front of the next block.
2930 */
2931 if ((int)dp->db_free < space_needed && db_idx == line_count - 1
2932 && lnum < buf->b_ml.ml_line_count)
2933 {
2934 /*
2935 * Now that the line is not going to be inserted in the block that we
2936 * expected, the line count has to be adjusted in the pointer blocks
2937 * by using ml_locked_lineadd.
2938 */
2939 --(buf->b_ml.ml_locked_lineadd);
2940 --(buf->b_ml.ml_locked_high);
2941 if ((hp = ml_find_line(buf, lnum + 1, ML_INSERT)) == NULL)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002942 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002943
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002944 db_idx = -1; // careful, it is negative!
2945 // get line count before the insertion
Bram Moolenaar071d4272004-06-13 20:20:40 +00002946 line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low;
2947 CHECK(buf->b_ml.ml_locked_low != lnum + 1, "locked_low != lnum + 1");
2948
2949 dp = (DATA_BL *)(hp->bh_data);
2950 }
2951
2952 ++buf->b_ml.ml_line_count;
2953
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002954 if ((int)dp->db_free >= space_needed) // enough room in data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002955 {
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002956 /*
2957 * Insert the new line in an existing data block, or in the data block
2958 * allocated above.
2959 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002960 dp->db_txt_start -= len;
2961 dp->db_free -= space_needed;
2962 ++(dp->db_line_count);
2963
2964 /*
2965 * move the text of the lines that follow to the front
2966 * adjust the indexes of the lines that follow
2967 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002968 if (line_count > db_idx + 1) // if there are following lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00002969 {
2970 /*
2971 * Offset is the start of the previous line.
2972 * This will become the character just after the new line.
2973 */
2974 if (db_idx < 0)
2975 offset = dp->db_txt_end;
2976 else
2977 offset = ((dp->db_index[db_idx]) & DB_INDEX_MASK);
2978 mch_memmove((char *)dp + dp->db_txt_start,
2979 (char *)dp + dp->db_txt_start + len,
2980 (size_t)(offset - (dp->db_txt_start + len)));
2981 for (i = line_count - 1; i > db_idx; --i)
2982 dp->db_index[i + 1] = dp->db_index[i] - len;
2983 dp->db_index[db_idx + 1] = offset - len;
2984 }
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002985 else
2986 // add line at the end (which is the start of the text)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002987 dp->db_index[db_idx + 1] = dp->db_txt_start;
2988
2989 /*
2990 * copy the text into the block
2991 */
2992 mch_memmove((char *)dp + dp->db_index[db_idx + 1], line, (size_t)len);
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002993 if (flags & ML_APPEND_MARK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002994 dp->db_index[db_idx + 1] |= DB_MARKED;
2995
2996 /*
2997 * Mark the block dirty.
2998 */
2999 buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003000 if (!(flags & ML_APPEND_NEW))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003001 buf->b_ml.ml_flags |= ML_LOCKED_POS;
3002 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003003 else // not enough space in data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003004 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003005 long line_count_left, line_count_right;
3006 int page_count_left, page_count_right;
3007 bhdr_T *hp_left;
3008 bhdr_T *hp_right;
3009 bhdr_T *hp_new;
3010 int lines_moved;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003011 int data_moved = 0; // init to shut up gcc
3012 int total_moved = 0; // init to shut up gcc
Bram Moolenaar071d4272004-06-13 20:20:40 +00003013 DATA_BL *dp_right, *dp_left;
3014 int stack_idx;
3015 int in_left;
3016 int lineadd;
3017 blocknr_T bnum_left, bnum_right;
3018 linenr_T lnum_left, lnum_right;
3019 int pb_idx;
3020 PTR_BL *pp_new;
3021
3022 /*
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003023 * There is not enough room, we have to create a new data block and
3024 * copy some lines into it.
3025 * Then we have to insert an entry in the pointer block.
3026 * If this pointer block also is full, we go up another block, and so
3027 * on, up to the root if necessary.
3028 * The line counts in the pointer blocks have already been adjusted by
3029 * ml_find_line().
3030 *
Bram Moolenaar071d4272004-06-13 20:20:40 +00003031 * We are going to allocate a new data block. Depending on the
3032 * situation it will be put to the left or right of the existing
3033 * block. If possible we put the new line in the left block and move
3034 * the lines after it to the right block. Otherwise the new line is
3035 * also put in the right block. This method is more efficient when
3036 * inserting a lot of lines at one place.
3037 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003038 if (db_idx < 0) // left block is new, right block is existing
Bram Moolenaar071d4272004-06-13 20:20:40 +00003039 {
3040 lines_moved = 0;
3041 in_left = TRUE;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003042 // space_needed does not change
Bram Moolenaar071d4272004-06-13 20:20:40 +00003043 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003044 else // left block is existing, right block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00003045 {
3046 lines_moved = line_count - db_idx - 1;
3047 if (lines_moved == 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003048 in_left = FALSE; // put new line in right block
3049 // space_needed does not change
Bram Moolenaar071d4272004-06-13 20:20:40 +00003050 else
3051 {
3052 data_moved = ((dp->db_index[db_idx]) & DB_INDEX_MASK) -
3053 dp->db_txt_start;
3054 total_moved = data_moved + lines_moved * INDEX_SIZE;
3055 if ((int)dp->db_free + total_moved >= space_needed)
3056 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003057 in_left = TRUE; // put new line in left block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003058 space_needed = total_moved;
3059 }
3060 else
3061 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003062 in_left = FALSE; // put new line in right block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003063 space_needed += total_moved;
3064 }
3065 }
3066 }
3067
3068 page_count = ((space_needed + HEADER_SIZE) + page_size - 1) / page_size;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003069 if ((hp_new = ml_new_data(mfp, flags & ML_APPEND_NEW, page_count))
3070 == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003071 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003072 // correct line counts in pointer blocks
Bram Moolenaar071d4272004-06-13 20:20:40 +00003073 --(buf->b_ml.ml_locked_lineadd);
3074 --(buf->b_ml.ml_locked_high);
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003075 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003076 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003077 if (db_idx < 0) // left block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00003078 {
3079 hp_left = hp_new;
3080 hp_right = hp;
3081 line_count_left = 0;
3082 line_count_right = line_count;
3083 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003084 else // right block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00003085 {
3086 hp_left = hp;
3087 hp_right = hp_new;
3088 line_count_left = line_count;
3089 line_count_right = 0;
3090 }
3091 dp_right = (DATA_BL *)(hp_right->bh_data);
3092 dp_left = (DATA_BL *)(hp_left->bh_data);
3093 bnum_left = hp_left->bh_bnum;
3094 bnum_right = hp_right->bh_bnum;
3095 page_count_left = hp_left->bh_page_count;
3096 page_count_right = hp_right->bh_page_count;
3097
3098 /*
3099 * May move the new line into the right/new block.
3100 */
3101 if (!in_left)
3102 {
3103 dp_right->db_txt_start -= len;
3104 dp_right->db_free -= len + INDEX_SIZE;
3105 dp_right->db_index[0] = dp_right->db_txt_start;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003106 if (flags & ML_APPEND_MARK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003107 dp_right->db_index[0] |= DB_MARKED;
3108
3109 mch_memmove((char *)dp_right + dp_right->db_txt_start,
3110 line, (size_t)len);
3111 ++line_count_right;
3112 }
3113 /*
3114 * may move lines from the left/old block to the right/new one.
3115 */
3116 if (lines_moved)
3117 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003118 dp_right->db_txt_start -= data_moved;
3119 dp_right->db_free -= total_moved;
3120 mch_memmove((char *)dp_right + dp_right->db_txt_start,
3121 (char *)dp_left + dp_left->db_txt_start,
3122 (size_t)data_moved);
3123 offset = dp_right->db_txt_start - dp_left->db_txt_start;
3124 dp_left->db_txt_start += data_moved;
3125 dp_left->db_free += total_moved;
3126
3127 /*
3128 * update indexes in the new block
3129 */
3130 for (to = line_count_right, from = db_idx + 1;
3131 from < line_count_left; ++from, ++to)
3132 dp_right->db_index[to] = dp->db_index[from] + offset;
3133 line_count_right += lines_moved;
3134 line_count_left -= lines_moved;
3135 }
3136
3137 /*
3138 * May move the new line into the left (old or new) block.
3139 */
3140 if (in_left)
3141 {
3142 dp_left->db_txt_start -= len;
3143 dp_left->db_free -= len + INDEX_SIZE;
3144 dp_left->db_index[line_count_left] = dp_left->db_txt_start;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003145 if (flags & ML_APPEND_MARK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003146 dp_left->db_index[line_count_left] |= DB_MARKED;
3147 mch_memmove((char *)dp_left + dp_left->db_txt_start,
3148 line, (size_t)len);
3149 ++line_count_left;
3150 }
3151
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003152 if (db_idx < 0) // left block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00003153 {
3154 lnum_left = lnum + 1;
3155 lnum_right = 0;
3156 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003157 else // right block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00003158 {
3159 lnum_left = 0;
3160 if (in_left)
3161 lnum_right = lnum + 2;
3162 else
3163 lnum_right = lnum + 1;
3164 }
3165 dp_left->db_line_count = line_count_left;
3166 dp_right->db_line_count = line_count_right;
3167
3168 /*
3169 * release the two data blocks
3170 * The new one (hp_new) already has a correct blocknumber.
3171 * The old one (hp, in ml_locked) gets a positive blocknumber if
3172 * we changed it and we are not editing a new file.
3173 */
3174 if (lines_moved || in_left)
3175 buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003176 if (!(flags & ML_APPEND_NEW) && db_idx >= 0 && in_left)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003177 buf->b_ml.ml_flags |= ML_LOCKED_POS;
3178 mf_put(mfp, hp_new, TRUE, FALSE);
3179
3180 /*
3181 * flush the old data block
3182 * set ml_locked_lineadd to 0, because the updating of the
3183 * pointer blocks is done below
3184 */
3185 lineadd = buf->b_ml.ml_locked_lineadd;
3186 buf->b_ml.ml_locked_lineadd = 0;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003187 ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003188
3189 /*
3190 * update pointer blocks for the new data block
3191 */
3192 for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0;
3193 --stack_idx)
3194 {
3195 ip = &(buf->b_ml.ml_stack[stack_idx]);
3196 pb_idx = ip->ip_index;
3197 if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003198 goto theend;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003199 pp = (PTR_BL *)(hp->bh_data); // must be pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003200 if (pp->pb_id != PTR_ID)
3201 {
Bram Moolenaareaaac012022-01-02 17:00:40 +00003202 iemsg(_(e_pointer_block_id_wrong_three));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003203 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003204 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003205 }
3206 /*
3207 * TODO: If the pointer block is full and we are adding at the end
3208 * try to insert in front of the next block
3209 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003210 // block not full, add one entry
Bram Moolenaar071d4272004-06-13 20:20:40 +00003211 if (pp->pb_count < pp->pb_count_max)
3212 {
3213 if (pb_idx + 1 < (int)pp->pb_count)
3214 mch_memmove(&pp->pb_pointer[pb_idx + 2],
3215 &pp->pb_pointer[pb_idx + 1],
3216 (size_t)(pp->pb_count - pb_idx - 1) * sizeof(PTR_EN));
3217 ++pp->pb_count;
3218 pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
3219 pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
3220 pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
3221 pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right;
3222 pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right;
3223 pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right;
3224
3225 if (lnum_left != 0)
3226 pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left;
3227 if (lnum_right != 0)
3228 pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right;
3229
3230 mf_put(mfp, hp, TRUE, FALSE);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003231 buf->b_ml.ml_stack_top = stack_idx + 1; // truncate stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00003232
3233 if (lineadd)
3234 {
3235 --(buf->b_ml.ml_stack_top);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003236 // fix line count for rest of blocks in the stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00003237 ml_lineadd(buf, lineadd);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003238 // fix stack itself
Bram Moolenaar071d4272004-06-13 20:20:40 +00003239 buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high +=
3240 lineadd;
3241 ++(buf->b_ml.ml_stack_top);
3242 }
3243
3244 /*
3245 * We are finished, break the loop here.
3246 */
3247 break;
3248 }
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01003249 // pointer block full
3250 /*
3251 * split the pointer block
3252 * allocate a new pointer block
3253 * move some of the pointer into the new block
3254 * prepare for updating the parent block
3255 */
3256 for (;;) // do this twice when splitting block 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00003257 {
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01003258 hp_new = ml_new_ptr(mfp);
3259 if (hp_new == NULL) // TODO: try to fix tree
3260 goto theend;
3261 pp_new = (PTR_BL *)(hp_new->bh_data);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003262
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01003263 if (hp->bh_bnum != 1)
3264 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003265
Bram Moolenaar071d4272004-06-13 20:20:40 +00003266 /*
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01003267 * if block 1 becomes full the tree is given an extra level
3268 * The pointers from block 1 are moved into the new block.
3269 * block 1 is updated to point to the new block
3270 * then continue to split the new block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003271 */
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01003272 mch_memmove(pp_new, pp, (size_t)page_size);
3273 pp->pb_count = 1;
3274 pp->pb_pointer[0].pe_bnum = hp_new->bh_bnum;
3275 pp->pb_pointer[0].pe_line_count = buf->b_ml.ml_line_count;
3276 pp->pb_pointer[0].pe_old_lnum = 1;
3277 pp->pb_pointer[0].pe_page_count = 1;
3278 mf_put(mfp, hp, TRUE, FALSE); // release block 1
3279 hp = hp_new; // new block is to be split
3280 pp = pp_new;
3281 CHECK(stack_idx != 0, _("stack_idx should be 0"));
3282 ip->ip_index = 0;
3283 ++stack_idx; // do block 1 again later
3284 }
3285 /*
3286 * move the pointers after the current one to the new block
3287 * If there are none, the new entry will be in the new block.
3288 */
3289 total_moved = pp->pb_count - pb_idx - 1;
3290 if (total_moved)
3291 {
3292 mch_memmove(&pp_new->pb_pointer[0],
Bram Moolenaar071d4272004-06-13 20:20:40 +00003293 &pp->pb_pointer[pb_idx + 1],
3294 (size_t)(total_moved) * sizeof(PTR_EN));
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01003295 pp_new->pb_count = total_moved;
3296 pp->pb_count -= total_moved - 1;
3297 pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right;
3298 pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right;
3299 pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right;
3300 if (lnum_right)
3301 pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003302 }
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01003303 else
3304 {
3305 pp_new->pb_count = 1;
3306 pp_new->pb_pointer[0].pe_bnum = bnum_right;
3307 pp_new->pb_pointer[0].pe_line_count = line_count_right;
3308 pp_new->pb_pointer[0].pe_page_count = page_count_right;
3309 pp_new->pb_pointer[0].pe_old_lnum = lnum_right;
3310 }
3311 pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
3312 pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
3313 pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
3314 if (lnum_left)
3315 pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left;
3316 lnum_left = 0;
3317 lnum_right = 0;
3318
3319 /*
3320 * recompute line counts
3321 */
3322 line_count_right = 0;
3323 for (i = 0; i < (int)pp_new->pb_count; ++i)
3324 line_count_right += pp_new->pb_pointer[i].pe_line_count;
3325 line_count_left = 0;
3326 for (i = 0; i < (int)pp->pb_count; ++i)
3327 line_count_left += pp->pb_pointer[i].pe_line_count;
3328
3329 bnum_left = hp->bh_bnum;
3330 bnum_right = hp_new->bh_bnum;
3331 page_count_left = 1;
3332 page_count_right = 1;
3333 mf_put(mfp, hp, TRUE, FALSE);
3334 mf_put(mfp, hp_new, TRUE, FALSE);
3335
Bram Moolenaar071d4272004-06-13 20:20:40 +00003336 }
3337
3338 /*
3339 * Safety check: fallen out of for loop?
3340 */
3341 if (stack_idx < 0)
3342 {
Bram Moolenaareaaac012022-01-02 17:00:40 +00003343 iemsg(_(e_updated_too_many_blocks));
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003344 buf->b_ml.ml_stack_top = 0; // invalidate stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00003345 }
3346 }
3347
3348#ifdef FEAT_BYTEOFF
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003349 // The line was inserted below 'lnum'
Bram Moolenaar50652b02022-08-07 21:48:37 +01003350 ml_updatechunk(buf, lnum + 1,
3351# ifdef FEAT_PROP_POPUP
3352 (long)text_len
3353# else
3354 (long)len
Bram Moolenaare5a0e8c2022-08-09 21:37:55 +01003355# endif
Bram Moolenaar50652b02022-08-07 21:48:37 +01003356 , ML_CHNK_ADDLINE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003357#endif
Bram Moolenaarcdd8a5e2021-08-25 16:40:03 +02003358
Bram Moolenaar071d4272004-06-13 20:20:40 +00003359#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02003360 if (netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +00003361 {
3362 if (STRLEN(line) > 0)
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003363 netbeans_inserted(buf, lnum+1, (colnr_T)0, line, (int)STRLEN(line));
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00003364 netbeans_inserted(buf, lnum+1, (colnr_T)STRLEN(line),
Bram Moolenaar071d4272004-06-13 20:20:40 +00003365 (char_u *)"\n", 1);
3366 }
3367#endif
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01003368#ifdef FEAT_JOB_CHANNEL
Bram Moolenaar99ef0622016-03-06 20:22:25 +01003369 if (buf->b_write_to_channel)
3370 channel_write_new_lines(buf);
3371#endif
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003372 ret = OK;
Bram Moolenaar99ef0622016-03-06 20:22:25 +01003373
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003374theend:
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003375#ifdef FEAT_PROP_POPUP
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003376 vim_free(tofree);
3377#endif
3378 return ret;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003379}
3380
3381/*
Bram Moolenaar250e3112019-07-15 23:02:14 +02003382 * Flush any pending change and call ml_append_int()
3383 */
3384 static int
3385ml_append_flush(
3386 buf_T *buf,
3387 linenr_T lnum, // append after this line (can be 0)
3388 char_u *line, // text of the new line
3389 colnr_T len, // length of line, including NUL, or 0
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003390 int flags) // ML_APPEND_ flags
Bram Moolenaar250e3112019-07-15 23:02:14 +02003391{
3392 if (lnum > buf->b_ml.ml_line_count)
3393 return FAIL; // lnum out of range
3394
3395 if (buf->b_ml.ml_line_lnum != 0)
3396 // This may also invoke ml_append_int().
3397 ml_flush_line(buf);
3398
3399#ifdef FEAT_EVAL
3400 // When inserting above recorded changes: flush the changes before changing
3401 // the text. Then flush the cached line, it may become invalid.
3402 may_invoke_listeners(buf, lnum + 1, lnum + 1, 1);
3403 if (buf->b_ml.ml_line_lnum != 0)
3404 ml_flush_line(buf);
3405#endif
3406
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003407 return ml_append_int(buf, lnum, line, len, flags);
Bram Moolenaar250e3112019-07-15 23:02:14 +02003408}
3409
3410/*
3411 * Append a line after lnum (may be 0 to insert a line in front of the file).
3412 * "line" does not need to be allocated, but can't be another line in a
3413 * buffer, unlocking may make it invalid.
3414 *
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003415 * "newfile": TRUE when starting to edit a new file, meaning that pe_old_lnum
Bram Moolenaar250e3112019-07-15 23:02:14 +02003416 * will be set for recovery
3417 * Check: The caller of this function should probably also call
3418 * appended_lines().
3419 *
3420 * return FAIL for failure, OK otherwise
3421 */
3422 int
3423ml_append(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003424 linenr_T lnum, // append after this line (can be 0)
3425 char_u *line, // text of the new line
3426 colnr_T len, // length of new line, including NUL, or 0
3427 int newfile) // flag, see above
Bram Moolenaar250e3112019-07-15 23:02:14 +02003428{
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003429 return ml_append_flags(lnum, line, len, newfile ? ML_APPEND_NEW : 0);
3430}
3431
3432 int
3433ml_append_flags(
3434 linenr_T lnum, // append after this line (can be 0)
3435 char_u *line, // text of the new line
3436 colnr_T len, // length of new line, including NUL, or 0
3437 int flags) // ML_APPEND_ values
3438{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003439 // When starting up, we might still need to create the memfile
Bram Moolenaar250e3112019-07-15 23:02:14 +02003440 if (curbuf->b_ml.ml_mfp == NULL && open_buffer(FALSE, NULL, 0) == FAIL)
3441 return FAIL;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003442 return ml_append_flush(curbuf, lnum, line, len, flags);
Bram Moolenaar250e3112019-07-15 23:02:14 +02003443}
3444
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003445
Bram Moolenaar9f28eeb2022-05-16 10:04:51 +01003446#if defined(FEAT_SPELL) || defined(FEAT_QUICKFIX) || defined(FEAT_PROP_POPUP) \
3447 || defined(PROTO)
Bram Moolenaar250e3112019-07-15 23:02:14 +02003448/*
3449 * Like ml_append() but for an arbitrary buffer. The buffer must already have
3450 * a memline.
3451 */
3452 int
3453ml_append_buf(
3454 buf_T *buf,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003455 linenr_T lnum, // append after this line (can be 0)
3456 char_u *line, // text of the new line
3457 colnr_T len, // length of new line, including NUL, or 0
3458 int newfile) // flag, see above
Bram Moolenaar250e3112019-07-15 23:02:14 +02003459{
3460 if (buf->b_ml.ml_mfp == NULL)
3461 return FAIL;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003462 return ml_append_flush(buf, lnum, line, len, newfile ? ML_APPEND_NEW : 0);
Bram Moolenaar250e3112019-07-15 23:02:14 +02003463}
3464#endif
3465
3466/*
Bram Moolenaar5966ea12020-07-15 15:30:05 +02003467 * Replace line "lnum", with buffering, in current buffer.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003468 *
Bram Moolenaar1056d982006-03-09 22:37:52 +00003469 * If "copy" is TRUE, make a copy of the line, otherwise the line has been
Bram Moolenaar071d4272004-06-13 20:20:40 +00003470 * copied to allocated memory already.
Bram Moolenaar5966ea12020-07-15 15:30:05 +02003471 * If "copy" is FALSE the "line" may be freed to add text properties!
3472 * Do not use it after calling ml_replace().
Bram Moolenaar071d4272004-06-13 20:20:40 +00003473 *
3474 * Check: The caller of this function should probably also call
Bram Moolenaara4d158b2022-08-14 14:17:45 +01003475 * changed_lines(), unless update_screen(UPD_NOT_VALID) is used.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003476 *
3477 * return FAIL for failure, OK otherwise
3478 */
3479 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003480ml_replace(linenr_T lnum, char_u *line, int copy)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003481{
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003482 colnr_T len = -1;
3483
3484 if (line != NULL)
Bram Moolenaar4efe73b2018-12-16 14:37:39 +01003485 len = (colnr_T)STRLEN(line);
Bram Moolenaarccae4672019-01-04 15:09:57 +01003486 return ml_replace_len(lnum, line, len, FALSE, copy);
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003487}
3488
Bram Moolenaarccae4672019-01-04 15:09:57 +01003489/*
3490 * Replace a line for the current buffer. Like ml_replace() with:
3491 * "len_arg" is the length of the text, excluding NUL.
3492 * If "has_props" is TRUE then "line_arg" includes the text properties and
3493 * "len_arg" includes the NUL of the text.
Bram Moolenaarfa4873c2022-06-30 22:13:59 +01003494 * When "copy" is TRUE copy the text into allocated memory, otherwise
3495 * "line_arg" must be allocated and will be consumed here.
Bram Moolenaarccae4672019-01-04 15:09:57 +01003496 */
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003497 int
Bram Moolenaarccae4672019-01-04 15:09:57 +01003498ml_replace_len(
3499 linenr_T lnum,
3500 char_u *line_arg,
3501 colnr_T len_arg,
3502 int has_props,
3503 int copy)
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003504{
3505 char_u *line = line_arg;
3506 colnr_T len = len_arg;
3507
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003508 if (line == NULL) // just checking...
Bram Moolenaar071d4272004-06-13 20:20:40 +00003509 return FAIL;
3510
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003511 // When starting up, we might still need to create the memfile
Bram Moolenaar59f931e2010-07-24 20:27:03 +02003512 if (curbuf->b_ml.ml_mfp == NULL && open_buffer(FALSE, NULL, 0) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003513 return FAIL;
3514
Bram Moolenaarccae4672019-01-04 15:09:57 +01003515 if (!has_props)
3516 ++len; // include the NUL after the text
3517 if (copy)
3518 {
3519 // copy the line to allocated memory
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003520#ifdef FEAT_PROP_POPUP
Bram Moolenaarccae4672019-01-04 15:09:57 +01003521 if (has_props)
3522 line = vim_memsave(line, len);
3523 else
3524#endif
3525 line = vim_strnsave(line, len - 1);
3526 if (line == NULL)
3527 return FAIL;
3528 }
3529
Bram Moolenaar071d4272004-06-13 20:20:40 +00003530#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02003531 if (netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +00003532 {
3533 netbeans_removed(curbuf, lnum, 0, (long)STRLEN(ml_get(lnum)));
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003534 netbeans_inserted(curbuf, lnum, 0, line, (int)STRLEN(line));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003535 }
3536#endif
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003537 if (curbuf->b_ml.ml_line_lnum != lnum)
3538 {
3539 // another line is buffered, flush it
3540 ml_flush_line(curbuf);
3541
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003542#ifdef FEAT_PROP_POPUP
Bram Moolenaarccae4672019-01-04 15:09:57 +01003543 if (curbuf->b_has_textprop && !has_props)
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003544 // Need to fetch the old line to copy over any text properties.
3545 ml_get_buf(curbuf, lnum, TRUE);
3546#endif
3547 }
3548
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003549#ifdef FEAT_PROP_POPUP
Bram Moolenaarccae4672019-01-04 15:09:57 +01003550 if (curbuf->b_has_textprop && !has_props)
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003551 {
3552 size_t oldtextlen = STRLEN(curbuf->b_ml.ml_line_ptr) + 1;
3553
3554 if (oldtextlen < (size_t)curbuf->b_ml.ml_line_len)
3555 {
3556 char_u *newline;
3557 size_t textproplen = curbuf->b_ml.ml_line_len - oldtextlen;
3558
3559 // Need to copy over text properties, stored after the text.
Bram Moolenaarccae4672019-01-04 15:09:57 +01003560 newline = alloc(len + (int)textproplen);
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003561 if (newline != NULL)
3562 {
Bram Moolenaarccae4672019-01-04 15:09:57 +01003563 mch_memmove(newline, line, len);
Bram Moolenaar0d3cb732019-05-18 13:05:18 +02003564 mch_memmove(newline + len, curbuf->b_ml.ml_line_ptr
3565 + oldtextlen, textproplen);
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003566 vim_free(line);
3567 line = newline;
Bram Moolenaar4efe73b2018-12-16 14:37:39 +01003568 len += (colnr_T)textproplen;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003569 }
3570 }
3571 }
3572#endif
3573
Bram Moolenaarfa4873c2022-06-30 22:13:59 +01003574 if (curbuf->b_ml.ml_flags & (ML_LINE_DIRTY | ML_ALLOCATED))
3575 vim_free(curbuf->b_ml.ml_line_ptr); // free allocated line
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003576
Bram Moolenaar071d4272004-06-13 20:20:40 +00003577 curbuf->b_ml.ml_line_ptr = line;
Bram Moolenaarccae4672019-01-04 15:09:57 +01003578 curbuf->b_ml.ml_line_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003579 curbuf->b_ml.ml_line_lnum = lnum;
3580 curbuf->b_ml.ml_flags = (curbuf->b_ml.ml_flags | ML_LINE_DIRTY) & ~ML_EMPTY;
3581
3582 return OK;
3583}
3584
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003585#ifdef FEAT_PROP_POPUP
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003586/*
3587 * Adjust text properties in line "lnum" for a deleted line.
Paul Ollis1bdc60e2022-05-15 22:24:55 +01003588 * When "above" is true this is the line above the deleted line, otherwise this
3589 * is the line below the deleted line.
3590 * "del_props[del_props_len]" are the properties of the deleted line.
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003591 */
3592 static void
3593adjust_text_props_for_delete(
3594 buf_T *buf,
3595 linenr_T lnum,
3596 char_u *del_props,
3597 int del_props_len,
3598 int above)
3599{
3600 int did_get_line = FALSE;
3601 int done_del;
3602 int done_this;
3603 textprop_T prop_del;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003604 bhdr_T *hp;
3605 DATA_BL *dp;
3606 int idx;
3607 int line_start;
3608 long line_size;
3609 int this_props_len;
3610 char_u *text;
3611 size_t textlen;
3612 int found;
3613
3614 for (done_del = 0; done_del < del_props_len; done_del += sizeof(textprop_T))
3615 {
3616 mch_memmove(&prop_del, del_props + done_del, sizeof(textprop_T));
3617 if ((above && (prop_del.tp_flags & TP_FLAG_CONT_PREV)
3618 && !(prop_del.tp_flags & TP_FLAG_CONT_NEXT))
3619 || (!above && (prop_del.tp_flags & TP_FLAG_CONT_NEXT)
3620 && !(prop_del.tp_flags & TP_FLAG_CONT_PREV)))
3621 {
3622 if (!did_get_line)
3623 {
3624 did_get_line = TRUE;
3625 if ((hp = ml_find_line(buf, lnum, ML_FIND)) == NULL)
3626 return;
3627
3628 dp = (DATA_BL *)(hp->bh_data);
3629 idx = lnum - buf->b_ml.ml_locked_low;
3630 line_start = ((dp->db_index[idx]) & DB_INDEX_MASK);
3631 if (idx == 0) // first line in block, text at the end
3632 line_size = dp->db_txt_end - line_start;
3633 else
Bram Moolenaar87be9be2020-05-30 15:32:02 +02003634 line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK)
3635 - line_start;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003636 text = (char_u *)dp + line_start;
3637 textlen = STRLEN(text) + 1;
3638 if ((long)textlen >= line_size)
3639 {
3640 if (above)
3641 internal_error("no text property above deleted line");
3642 else
3643 internal_error("no text property below deleted line");
3644 return;
3645 }
Bram Moolenaar4b7214e2019-01-03 21:55:32 +01003646 this_props_len = line_size - (int)textlen;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003647 }
3648
3649 found = FALSE;
Bram Moolenaar87be9be2020-05-30 15:32:02 +02003650 for (done_this = 0; done_this < this_props_len;
3651 done_this += sizeof(textprop_T))
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003652 {
Bram Moolenaar87be9be2020-05-30 15:32:02 +02003653 int flag = above ? TP_FLAG_CONT_NEXT
3654 : TP_FLAG_CONT_PREV;
3655 textprop_T prop_this;
3656
Paul Ollis1bdc60e2022-05-15 22:24:55 +01003657 mch_memmove(&prop_this, text + textlen + done_this,
Bram Moolenaar87be9be2020-05-30 15:32:02 +02003658 sizeof(textprop_T));
3659 if ((prop_this.tp_flags & flag)
3660 && prop_del.tp_id == prop_this.tp_id
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003661 && prop_del.tp_type == prop_this.tp_type)
3662 {
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003663 found = TRUE;
Bram Moolenaar87be9be2020-05-30 15:32:02 +02003664 prop_this.tp_flags &= ~flag;
Paul Ollis1bdc60e2022-05-15 22:24:55 +01003665 mch_memmove(text + textlen + done_this, &prop_this,
Bram Moolenaar87be9be2020-05-30 15:32:02 +02003666 sizeof(textprop_T));
3667 break;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003668 }
3669 }
3670 if (!found)
3671 {
3672 if (above)
3673 internal_error("text property above deleted line not found");
3674 else
3675 internal_error("text property below deleted line not found");
3676 }
3677
3678 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
3679 }
3680 }
3681}
3682#endif
3683
Bram Moolenaar071d4272004-06-13 20:20:40 +00003684/*
Bram Moolenaar4033c552017-09-16 20:54:51 +02003685 * Delete line "lnum" in the current buffer.
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003686 * When "flags" has ML_DEL_MESSAGE may give a "No lines in buffer" message.
3687 * When "flags" has ML_DEL_UNDO this is called from undo.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003688 *
3689 * return FAIL for failure, OK otherwise
3690 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003691 static int
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003692ml_delete_int(buf_T *buf, linenr_T lnum, int flags)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003693{
3694 bhdr_T *hp;
3695 memfile_T *mfp;
3696 DATA_BL *dp;
3697 PTR_BL *pp;
3698 infoptr_T *ip;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003699 int count; // number of entries in block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003700 int idx;
3701 int stack_idx;
3702 int text_start;
3703 int line_start;
3704 long line_size;
3705 int i;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003706 int ret = FAIL;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003707#ifdef FEAT_PROP_POPUP
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003708 char_u *textprop_save = NULL;
Bram Moolenaare5a0e8c2022-08-09 21:37:55 +01003709 long textprop_len = 0;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003710#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003711
Bram Moolenaar071d4272004-06-13 20:20:40 +00003712 if (lowest_marked && lowest_marked > lnum)
3713 lowest_marked--;
3714
3715/*
3716 * If the file becomes empty the last line is replaced by an empty line.
3717 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003718 if (buf->b_ml.ml_line_count == 1) // file becomes empty
Bram Moolenaar071d4272004-06-13 20:20:40 +00003719 {
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003720 if ((flags & ML_DEL_MESSAGE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003721#ifdef FEAT_NETBEANS_INTG
3722 && !netbeansSuppressNoLines
3723#endif
3724 )
Bram Moolenaar238a5642006-02-21 22:12:05 +00003725 set_keep_msg((char_u *)_(no_lines_msg), 0);
3726
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003727 // FEAT_BYTEOFF already handled in there, don't worry 'bout it below
Bram Moolenaar071d4272004-06-13 20:20:40 +00003728 i = ml_replace((linenr_T)1, (char_u *)"", TRUE);
3729 buf->b_ml.ml_flags |= ML_EMPTY;
3730
3731 return i;
3732 }
3733
3734/*
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003735 * Find the data block containing the line.
3736 * This also fills the stack with the blocks from the root to the data block.
3737 * This also releases any locked block..
Bram Moolenaar071d4272004-06-13 20:20:40 +00003738 */
3739 mfp = buf->b_ml.ml_mfp;
3740 if (mfp == NULL)
3741 return FAIL;
3742
3743 if ((hp = ml_find_line(buf, lnum, ML_DELETE)) == NULL)
3744 return FAIL;
3745
3746 dp = (DATA_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003747 // compute line count before the delete
Bram Moolenaar071d4272004-06-13 20:20:40 +00003748 count = (long)(buf->b_ml.ml_locked_high)
3749 - (long)(buf->b_ml.ml_locked_low) + 2;
3750 idx = lnum - buf->b_ml.ml_locked_low;
3751
3752 --buf->b_ml.ml_line_count;
3753
3754 line_start = ((dp->db_index[idx]) & DB_INDEX_MASK);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003755 if (idx == 0) // first line in block, text at the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00003756 line_size = dp->db_txt_end - line_start;
3757 else
3758 line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK) - line_start;
3759
3760#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02003761 if (netbeans_active())
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00003762 netbeans_removed(buf, lnum, 0, line_size);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003763#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003764#ifdef FEAT_PROP_POPUP
Bram Moolenaare5a0e8c2022-08-09 21:37:55 +01003765 // If there are text properties compute their byte length.
3766 // if needed make a copy, so that we can update properties in preceding and
3767 // following lines.
3768 if (buf->b_has_textprop)
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003769 {
3770 size_t textlen = STRLEN((char_u *)dp + line_start) + 1;
3771
Bram Moolenaare5a0e8c2022-08-09 21:37:55 +01003772 textprop_len = line_size - (long)textlen;
3773 if (!(flags & (ML_DEL_UNDO | ML_DEL_NOPROP)) && textprop_len > 0)
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003774 textprop_save = vim_memsave((char_u *)dp + line_start + textlen,
Bram Moolenaare5a0e8c2022-08-09 21:37:55 +01003775 textprop_len);
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003776 }
3777#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003778
3779/*
3780 * special case: If there is only one line in the data block it becomes empty.
3781 * Then we have to remove the entry, pointing to this data block, from the
3782 * pointer block. If this pointer block also becomes empty, we go up another
3783 * block, and so on, up to the root if necessary.
3784 * The line counts in the pointer blocks have already been adjusted by
3785 * ml_find_line().
3786 */
3787 if (count == 1)
3788 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003789 mf_free(mfp, hp); // free the data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003790 buf->b_ml.ml_locked = NULL;
3791
Bram Moolenaare60acc12011-05-10 16:41:25 +02003792 for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0;
3793 --stack_idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003794 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003795 buf->b_ml.ml_stack_top = 0; // stack is invalid when failing
Bram Moolenaar071d4272004-06-13 20:20:40 +00003796 ip = &(buf->b_ml.ml_stack[stack_idx]);
3797 idx = ip->ip_index;
3798 if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003799 goto theend;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003800 pp = (PTR_BL *)(hp->bh_data); // must be pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003801 if (pp->pb_id != PTR_ID)
3802 {
Bram Moolenaareaaac012022-01-02 17:00:40 +00003803 iemsg(_(e_pointer_block_id_wrong_four));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003804 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003805 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003806 }
3807 count = --(pp->pb_count);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003808 if (count == 0) // the pointer block becomes empty!
Bram Moolenaar071d4272004-06-13 20:20:40 +00003809 mf_free(mfp, hp);
3810 else
3811 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003812 if (count != idx) // move entries after the deleted one
Bram Moolenaar071d4272004-06-13 20:20:40 +00003813 mch_memmove(&pp->pb_pointer[idx], &pp->pb_pointer[idx + 1],
3814 (size_t)(count - idx) * sizeof(PTR_EN));
3815 mf_put(mfp, hp, TRUE, FALSE);
3816
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003817 buf->b_ml.ml_stack_top = stack_idx; // truncate stack
3818 // fix line count for rest of blocks in the stack
Bram Moolenaar6b803a72007-05-06 14:25:46 +00003819 if (buf->b_ml.ml_locked_lineadd != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003820 {
3821 ml_lineadd(buf, buf->b_ml.ml_locked_lineadd);
3822 buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high +=
Bram Moolenaar6b803a72007-05-06 14:25:46 +00003823 buf->b_ml.ml_locked_lineadd;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003824 }
3825 ++(buf->b_ml.ml_stack_top);
3826
3827 break;
3828 }
3829 }
3830 CHECK(stack_idx < 0, _("deleted block 1?"));
3831 }
3832 else
3833 {
3834 /*
3835 * delete the text by moving the next lines forwards
3836 */
3837 text_start = dp->db_txt_start;
3838 mch_memmove((char *)dp + text_start + line_size,
3839 (char *)dp + text_start, (size_t)(line_start - text_start));
3840
3841 /*
3842 * delete the index by moving the next indexes backwards
3843 * Adjust the indexes for the text movement.
3844 */
3845 for (i = idx; i < count - 1; ++i)
3846 dp->db_index[i] = dp->db_index[i + 1] + line_size;
3847
3848 dp->db_free += line_size + INDEX_SIZE;
3849 dp->db_txt_start += line_size;
3850 --(dp->db_line_count);
3851
3852 /*
3853 * mark the block dirty and make sure it is in the file (for recovery)
3854 */
3855 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
3856 }
3857
3858#ifdef FEAT_BYTEOFF
Bram Moolenaarcdd8a5e2021-08-25 16:40:03 +02003859 ml_updatechunk(buf, lnum, line_size
3860# ifdef FEAT_PROP_POPUP
Bram Moolenaare5a0e8c2022-08-09 21:37:55 +01003861 - textprop_len
Bram Moolenaarcdd8a5e2021-08-25 16:40:03 +02003862# endif
Bram Moolenaare5a0e8c2022-08-09 21:37:55 +01003863 , ML_CHNK_DELLINE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003864#endif
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003865 ret = OK;
3866
3867theend:
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003868#ifdef FEAT_PROP_POPUP
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003869 if (textprop_save != NULL)
3870 {
3871 // Adjust text properties in the line above and below.
3872 if (lnum > 1)
Bram Moolenaar4cd5c522021-06-27 13:04:00 +02003873 adjust_text_props_for_delete(buf, lnum - 1, textprop_save,
Bram Moolenaare5a0e8c2022-08-09 21:37:55 +01003874 (int)textprop_len, TRUE);
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003875 if (lnum <= buf->b_ml.ml_line_count)
Bram Moolenaar4cd5c522021-06-27 13:04:00 +02003876 adjust_text_props_for_delete(buf, lnum, textprop_save,
Bram Moolenaare5a0e8c2022-08-09 21:37:55 +01003877 (int)textprop_len, FALSE);
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003878 }
3879 vim_free(textprop_save);
3880#endif
3881 return ret;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003882}
3883
3884/*
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003885 * Delete line "lnum" in the current buffer.
3886 * When "message" is TRUE may give a "No lines in buffer" message.
3887 *
3888 * Check: The caller of this function should probably also call
3889 * deleted_lines() after this.
3890 *
3891 * return FAIL for failure, OK otherwise
3892 */
3893 int
Bram Moolenaarca70c072020-05-30 20:30:46 +02003894ml_delete(linenr_T lnum)
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003895{
Bram Moolenaarca70c072020-05-30 20:30:46 +02003896 return ml_delete_flags(lnum, 0);
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003897}
3898
3899/*
3900 * Like ml_delete() but using flags (see ml_delete_int()).
3901 */
3902 int
3903ml_delete_flags(linenr_T lnum, int flags)
3904{
3905 ml_flush_line(curbuf);
3906 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count)
3907 return FAIL;
3908
3909#ifdef FEAT_EVAL
3910 // When inserting above recorded changes: flush the changes before changing
3911 // the text.
3912 may_invoke_listeners(curbuf, lnum, lnum + 1, -1);
3913#endif
3914
3915 return ml_delete_int(curbuf, lnum, flags);
3916}
3917
3918/*
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003919 * set the DB_MARKED flag for line 'lnum'
Bram Moolenaar071d4272004-06-13 20:20:40 +00003920 */
3921 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003922ml_setmarked(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003923{
3924 bhdr_T *hp;
3925 DATA_BL *dp;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003926 // invalid line number
Bram Moolenaar071d4272004-06-13 20:20:40 +00003927 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count
3928 || curbuf->b_ml.ml_mfp == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003929 return; // give error message?
Bram Moolenaar071d4272004-06-13 20:20:40 +00003930
3931 if (lowest_marked == 0 || lowest_marked > lnum)
3932 lowest_marked = lnum;
3933
3934 /*
3935 * find the data block containing the line
3936 * This also fills the stack with the blocks from the root to the data block
3937 * This also releases any locked block.
3938 */
3939 if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003940 return; // give error message?
Bram Moolenaar071d4272004-06-13 20:20:40 +00003941
3942 dp = (DATA_BL *)(hp->bh_data);
3943 dp->db_index[lnum - curbuf->b_ml.ml_locked_low] |= DB_MARKED;
3944 curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
3945}
3946
3947/*
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003948 * find the first line with its DB_MARKED flag set
Bram Moolenaar071d4272004-06-13 20:20:40 +00003949 */
3950 linenr_T
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003951ml_firstmarked(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003952{
3953 bhdr_T *hp;
3954 DATA_BL *dp;
3955 linenr_T lnum;
3956 int i;
3957
3958 if (curbuf->b_ml.ml_mfp == NULL)
3959 return (linenr_T) 0;
3960
3961 /*
3962 * The search starts with lowest_marked line. This is the last line where
3963 * a mark was found, adjusted by inserting/deleting lines.
3964 */
3965 for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count; )
3966 {
3967 /*
3968 * Find the data block containing the line.
3969 * This also fills the stack with the blocks from the root to the data
3970 * block This also releases any locked block.
3971 */
3972 if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003973 return (linenr_T)0; // give error message?
Bram Moolenaar071d4272004-06-13 20:20:40 +00003974
3975 dp = (DATA_BL *)(hp->bh_data);
3976
3977 for (i = lnum - curbuf->b_ml.ml_locked_low;
3978 lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum)
3979 if ((dp->db_index[i]) & DB_MARKED)
3980 {
3981 (dp->db_index[i]) &= DB_INDEX_MASK;
3982 curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
3983 lowest_marked = lnum + 1;
3984 return lnum;
3985 }
3986 }
3987
3988 return (linenr_T) 0;
3989}
3990
Bram Moolenaar071d4272004-06-13 20:20:40 +00003991/*
3992 * clear all DB_MARKED flags
3993 */
3994 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003995ml_clearmarked(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003996{
3997 bhdr_T *hp;
3998 DATA_BL *dp;
3999 linenr_T lnum;
4000 int i;
4001
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004002 if (curbuf->b_ml.ml_mfp == NULL) // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00004003 return;
4004
4005 /*
4006 * The search starts with line lowest_marked.
4007 */
4008 for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count; )
4009 {
4010 /*
4011 * Find the data block containing the line.
4012 * This also fills the stack with the blocks from the root to the data
4013 * block and releases any locked block.
4014 */
4015 if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004016 return; // give error message?
Bram Moolenaar071d4272004-06-13 20:20:40 +00004017
4018 dp = (DATA_BL *)(hp->bh_data);
4019
4020 for (i = lnum - curbuf->b_ml.ml_locked_low;
4021 lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum)
4022 if ((dp->db_index[i]) & DB_MARKED)
4023 {
4024 (dp->db_index[i]) &= DB_INDEX_MASK;
4025 curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
4026 }
4027 }
4028
4029 lowest_marked = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004030}
4031
4032/*
4033 * flush ml_line if necessary
4034 */
4035 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004036ml_flush_line(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004037{
4038 bhdr_T *hp;
4039 DATA_BL *dp;
4040 linenr_T lnum;
4041 char_u *new_line;
4042 char_u *old_line;
4043 colnr_T new_len;
4044 int old_len;
4045 int extra;
4046 int idx;
4047 int start;
4048 int count;
4049 int i;
Bram Moolenaar0ca4b352010-02-11 18:54:43 +01004050 static int entered = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004051
4052 if (buf->b_ml.ml_line_lnum == 0 || buf->b_ml.ml_mfp == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004053 return; // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00004054
4055 if (buf->b_ml.ml_flags & ML_LINE_DIRTY)
4056 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004057 // This code doesn't work recursively, but Netbeans may call back here
4058 // when obtaining the cursor position.
Bram Moolenaar0ca4b352010-02-11 18:54:43 +01004059 if (entered)
4060 return;
4061 entered = TRUE;
4062
Bram Moolenaar071d4272004-06-13 20:20:40 +00004063 lnum = buf->b_ml.ml_line_lnum;
4064 new_line = buf->b_ml.ml_line_ptr;
4065
4066 hp = ml_find_line(buf, lnum, ML_FIND);
4067 if (hp == NULL)
Bram Moolenaareaaac012022-01-02 17:00:40 +00004068 siemsg(_(e_cannot_find_line_nr), lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004069 else
4070 {
4071 dp = (DATA_BL *)(hp->bh_data);
4072 idx = lnum - buf->b_ml.ml_locked_low;
4073 start = ((dp->db_index[idx]) & DB_INDEX_MASK);
4074 old_line = (char_u *)dp + start;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004075 if (idx == 0) // line is last in block
Bram Moolenaar071d4272004-06-13 20:20:40 +00004076 old_len = dp->db_txt_end - start;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004077 else // text of previous line follows
Bram Moolenaar071d4272004-06-13 20:20:40 +00004078 old_len = (dp->db_index[idx - 1] & DB_INDEX_MASK) - start;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01004079 new_len = buf->b_ml.ml_line_len;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004080 extra = new_len - old_len; // negative if lines gets smaller
Bram Moolenaar071d4272004-06-13 20:20:40 +00004081
4082 /*
4083 * if new line fits in data block, replace directly
4084 */
4085 if ((int)dp->db_free >= extra)
4086 {
Bram Moolenaar92755bb2021-08-15 22:18:04 +02004087#if defined(FEAT_BYTEOFF) && defined(FEAT_PROP_POPUP)
Bram Moolenaar14c75302021-08-15 14:28:40 +02004088 int old_prop_len = 0;
Paul Ollis4c3d21a2022-05-24 21:26:37 +01004089 if (buf->b_has_textprop)
4090 old_prop_len = old_len - (int)STRLEN(old_line) - 1;
Bram Moolenaar14c75302021-08-15 14:28:40 +02004091#endif
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004092 // if the length changes and there are following lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00004093 count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 1;
4094 if (extra != 0 && idx < count - 1)
4095 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004096 // move text of following lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00004097 mch_memmove((char *)dp + dp->db_txt_start - extra,
4098 (char *)dp + dp->db_txt_start,
4099 (size_t)(start - dp->db_txt_start));
4100
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004101 // adjust pointers of this and following lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00004102 for (i = idx + 1; i < count; ++i)
4103 dp->db_index[i] -= extra;
4104 }
4105 dp->db_index[idx] -= extra;
4106
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004107 // adjust free space
Bram Moolenaar071d4272004-06-13 20:20:40 +00004108 dp->db_free -= extra;
4109 dp->db_txt_start -= extra;
4110
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004111 // copy new line into the data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00004112 mch_memmove(old_line - extra, new_line, (size_t)new_len);
4113 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
Bram Moolenaar92755bb2021-08-15 22:18:04 +02004114#if defined(FEAT_BYTEOFF) && defined(FEAT_PROP_POPUP)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004115 // The else case is already covered by the insert and delete
Bram Moolenaar14c75302021-08-15 14:28:40 +02004116 if (buf->b_has_textprop)
4117 {
4118 // Do not count the size of any text properties.
4119 extra += old_prop_len;
Bram Moolenaar434df7a2021-08-16 21:15:32 +02004120 extra -= new_len - (int)STRLEN(new_line) - 1;
Bram Moolenaar14c75302021-08-15 14:28:40 +02004121 }
4122 if (extra != 0)
4123 ml_updatechunk(buf, lnum, (long)extra, ML_CHNK_UPDLINE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004124#endif
4125 }
4126 else
4127 {
4128 /*
4129 * Cannot do it in one data block: Delete and append.
4130 * Append first, because ml_delete_int() cannot delete the
4131 * last line in a buffer, which causes trouble for a buffer
4132 * that has only one line.
4133 * Don't forget to copy the mark!
4134 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004135 // How about handling errors???
Bram Moolenaara9d4b842020-05-30 14:46:52 +02004136 (void)ml_append_int(buf, lnum, new_line, new_len,
Bram Moolenaar840f91f2021-05-26 22:32:10 +02004137 ((dp->db_index[idx] & DB_MARKED) ? ML_APPEND_MARK : 0)
4138#ifdef FEAT_PROP_POPUP
4139 | ML_APPEND_NOPROP
4140#endif
4141 );
Bram Moolenaar4cd5c522021-06-27 13:04:00 +02004142 (void)ml_delete_int(buf, lnum, ML_DEL_NOPROP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004143 }
4144 }
4145 vim_free(new_line);
Bram Moolenaar0ca4b352010-02-11 18:54:43 +01004146
4147 entered = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004148 }
Bram Moolenaarfa4873c2022-06-30 22:13:59 +01004149 else if (buf->b_ml.ml_flags & ML_ALLOCATED)
4150 vim_free(buf->b_ml.ml_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004151
Bram Moolenaarfa4873c2022-06-30 22:13:59 +01004152 buf->b_ml.ml_flags &= ~(ML_LINE_DIRTY | ML_ALLOCATED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004153 buf->b_ml.ml_line_lnum = 0;
4154}
4155
4156/*
4157 * create a new, empty, data block
4158 */
4159 static bhdr_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004160ml_new_data(memfile_T *mfp, int negative, int page_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004161{
4162 bhdr_T *hp;
4163 DATA_BL *dp;
4164
4165 if ((hp = mf_new(mfp, negative, page_count)) == NULL)
4166 return NULL;
4167
4168 dp = (DATA_BL *)(hp->bh_data);
4169 dp->db_id = DATA_ID;
4170 dp->db_txt_start = dp->db_txt_end = page_count * mfp->mf_page_size;
4171 dp->db_free = dp->db_txt_start - HEADER_SIZE;
4172 dp->db_line_count = 0;
4173
4174 return hp;
4175}
4176
4177/*
4178 * create a new, empty, pointer block
4179 */
4180 static bhdr_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004181ml_new_ptr(memfile_T *mfp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004182{
4183 bhdr_T *hp;
4184 PTR_BL *pp;
4185
4186 if ((hp = mf_new(mfp, FALSE, 1)) == NULL)
4187 return NULL;
4188
4189 pp = (PTR_BL *)(hp->bh_data);
4190 pp->pb_id = PTR_ID;
4191 pp->pb_count = 0;
Bram Moolenaarb67ba032023-04-22 21:14:26 +01004192 pp->pb_count_max = PB_COUNT_MAX(mfp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004193
4194 return hp;
4195}
4196
4197/*
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01004198 * Lookup line 'lnum' in a memline.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004199 *
4200 * action: if ML_DELETE or ML_INSERT the line count is updated while searching
4201 * if ML_FLUSH only flush a locked block
4202 * if ML_FIND just find the line
4203 *
4204 * If the block was found it is locked and put in ml_locked.
4205 * The stack is updated to lead to the locked block. The ip_high field in
4206 * the stack is updated to reflect the last line in the block AFTER the
4207 * insert or delete, also if the pointer block has not been updated yet. But
Bram Moolenaar6b803a72007-05-06 14:25:46 +00004208 * if ml_locked != NULL ml_locked_lineadd must be added to ip_high.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004209 *
4210 * return: NULL for failure, pointer to block header otherwise
4211 */
4212 static bhdr_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004213ml_find_line(buf_T *buf, linenr_T lnum, int action)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004214{
4215 DATA_BL *dp;
4216 PTR_BL *pp;
4217 infoptr_T *ip;
4218 bhdr_T *hp;
4219 memfile_T *mfp;
4220 linenr_T t;
4221 blocknr_T bnum, bnum2;
4222 int dirty;
4223 linenr_T low, high;
4224 int top;
4225 int page_count;
4226 int idx;
4227
4228 mfp = buf->b_ml.ml_mfp;
4229
4230 /*
4231 * If there is a locked block check if the wanted line is in it.
4232 * If not, flush and release the locked block.
4233 * Don't do this for ML_INSERT_SAME, because the stack need to be updated.
4234 * Don't do this for ML_FLUSH, because we want to flush the locked block.
Bram Moolenaar47b8b152007-02-07 02:41:57 +00004235 * Don't do this when 'swapfile' is reset, we want to load all the blocks.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004236 */
4237 if (buf->b_ml.ml_locked)
4238 {
Bram Moolenaar47b8b152007-02-07 02:41:57 +00004239 if (ML_SIMPLE(action)
4240 && buf->b_ml.ml_locked_low <= lnum
4241 && buf->b_ml.ml_locked_high >= lnum
4242 && !mf_dont_release)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004243 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004244 // remember to update pointer blocks and stack later
Bram Moolenaar071d4272004-06-13 20:20:40 +00004245 if (action == ML_INSERT)
4246 {
4247 ++(buf->b_ml.ml_locked_lineadd);
4248 ++(buf->b_ml.ml_locked_high);
4249 }
4250 else if (action == ML_DELETE)
4251 {
4252 --(buf->b_ml.ml_locked_lineadd);
4253 --(buf->b_ml.ml_locked_high);
4254 }
4255 return (buf->b_ml.ml_locked);
4256 }
4257
4258 mf_put(mfp, buf->b_ml.ml_locked, buf->b_ml.ml_flags & ML_LOCKED_DIRTY,
4259 buf->b_ml.ml_flags & ML_LOCKED_POS);
4260 buf->b_ml.ml_locked = NULL;
4261
Bram Moolenaar6b803a72007-05-06 14:25:46 +00004262 /*
4263 * If lines have been added or deleted in the locked block, need to
4264 * update the line count in pointer blocks.
4265 */
4266 if (buf->b_ml.ml_locked_lineadd != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004267 ml_lineadd(buf, buf->b_ml.ml_locked_lineadd);
4268 }
4269
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004270 if (action == ML_FLUSH) // nothing else to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00004271 return NULL;
4272
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004273 bnum = 1; // start at the root of the tree
Bram Moolenaar071d4272004-06-13 20:20:40 +00004274 page_count = 1;
4275 low = 1;
4276 high = buf->b_ml.ml_line_count;
4277
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004278 if (action == ML_FIND) // first try stack entries
Bram Moolenaar071d4272004-06-13 20:20:40 +00004279 {
4280 for (top = buf->b_ml.ml_stack_top - 1; top >= 0; --top)
4281 {
4282 ip = &(buf->b_ml.ml_stack[top]);
4283 if (ip->ip_low <= lnum && ip->ip_high >= lnum)
4284 {
4285 bnum = ip->ip_bnum;
4286 low = ip->ip_low;
4287 high = ip->ip_high;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004288 buf->b_ml.ml_stack_top = top; // truncate stack at prev entry
Bram Moolenaar071d4272004-06-13 20:20:40 +00004289 break;
4290 }
4291 }
4292 if (top < 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004293 buf->b_ml.ml_stack_top = 0; // not found, start at the root
Bram Moolenaar071d4272004-06-13 20:20:40 +00004294 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004295 else // ML_DELETE or ML_INSERT
4296 buf->b_ml.ml_stack_top = 0; // start at the root
Bram Moolenaar071d4272004-06-13 20:20:40 +00004297
4298/*
4299 * search downwards in the tree until a data block is found
4300 */
4301 for (;;)
4302 {
4303 if ((hp = mf_get(mfp, bnum, page_count)) == NULL)
4304 goto error_noblock;
4305
4306 /*
4307 * update high for insert/delete
4308 */
4309 if (action == ML_INSERT)
4310 ++high;
4311 else if (action == ML_DELETE)
4312 --high;
4313
4314 dp = (DATA_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004315 if (dp->db_id == DATA_ID) // data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00004316 {
4317 buf->b_ml.ml_locked = hp;
4318 buf->b_ml.ml_locked_low = low;
4319 buf->b_ml.ml_locked_high = high;
4320 buf->b_ml.ml_locked_lineadd = 0;
4321 buf->b_ml.ml_flags &= ~(ML_LOCKED_DIRTY | ML_LOCKED_POS);
4322 return hp;
4323 }
4324
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004325 pp = (PTR_BL *)(dp); // must be pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00004326 if (pp->pb_id != PTR_ID)
4327 {
Bram Moolenaareaaac012022-01-02 17:00:40 +00004328 iemsg(_(e_pointer_block_id_wrong));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004329 goto error_block;
4330 }
4331
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004332 if ((top = ml_add_stack(buf)) < 0) // add new entry to stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00004333 goto error_block;
4334 ip = &(buf->b_ml.ml_stack[top]);
4335 ip->ip_bnum = bnum;
4336 ip->ip_low = low;
4337 ip->ip_high = high;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004338 ip->ip_index = -1; // index not known yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00004339
4340 dirty = FALSE;
4341 for (idx = 0; idx < (int)pp->pb_count; ++idx)
4342 {
4343 t = pp->pb_pointer[idx].pe_line_count;
4344 CHECK(t == 0, _("pe_line_count is zero"));
4345 if ((low += t) > lnum)
4346 {
4347 ip->ip_index = idx;
4348 bnum = pp->pb_pointer[idx].pe_bnum;
4349 page_count = pp->pb_pointer[idx].pe_page_count;
4350 high = low - 1;
4351 low -= t;
4352
4353 /*
4354 * a negative block number may have been changed
4355 */
4356 if (bnum < 0)
4357 {
4358 bnum2 = mf_trans_del(mfp, bnum);
4359 if (bnum != bnum2)
4360 {
4361 bnum = bnum2;
4362 pp->pb_pointer[idx].pe_bnum = bnum;
4363 dirty = TRUE;
4364 }
4365 }
4366
4367 break;
4368 }
4369 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004370 if (idx >= (int)pp->pb_count) // past the end: something wrong!
Bram Moolenaar071d4272004-06-13 20:20:40 +00004371 {
4372 if (lnum > buf->b_ml.ml_line_count)
Bram Moolenaareaaac012022-01-02 17:00:40 +00004373 siemsg(_(e_line_number_out_of_range_nr_past_the_end),
Bram Moolenaar071d4272004-06-13 20:20:40 +00004374 lnum - buf->b_ml.ml_line_count);
4375
4376 else
Bram Moolenaareaaac012022-01-02 17:00:40 +00004377 siemsg(_(e_line_count_wrong_in_block_nr), bnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004378 goto error_block;
4379 }
4380 if (action == ML_DELETE)
4381 {
4382 pp->pb_pointer[idx].pe_line_count--;
4383 dirty = TRUE;
4384 }
4385 else if (action == ML_INSERT)
4386 {
4387 pp->pb_pointer[idx].pe_line_count++;
4388 dirty = TRUE;
4389 }
4390 mf_put(mfp, hp, dirty, FALSE);
4391 }
4392
4393error_block:
4394 mf_put(mfp, hp, FALSE, FALSE);
4395error_noblock:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02004396 /*
4397 * If action is ML_DELETE or ML_INSERT we have to correct the tree for
4398 * the incremented/decremented line counts, because there won't be a line
4399 * inserted/deleted after all.
4400 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004401 if (action == ML_DELETE)
4402 ml_lineadd(buf, 1);
4403 else if (action == ML_INSERT)
4404 ml_lineadd(buf, -1);
4405 buf->b_ml.ml_stack_top = 0;
4406 return NULL;
4407}
4408
4409/*
4410 * add an entry to the info pointer stack
4411 *
4412 * return -1 for failure, number of the new entry otherwise
4413 */
4414 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004415ml_add_stack(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004416{
4417 int top;
4418 infoptr_T *newstack;
4419
4420 top = buf->b_ml.ml_stack_top;
4421
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004422 // may have to increase the stack size
Bram Moolenaar071d4272004-06-13 20:20:40 +00004423 if (top == buf->b_ml.ml_stack_size)
4424 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004425 CHECK(top > 0, _("Stack size increases")); // more than 5 levels???
Bram Moolenaar071d4272004-06-13 20:20:40 +00004426
Bram Moolenaarc799fe22019-05-28 23:08:19 +02004427 newstack = ALLOC_MULT(infoptr_T, buf->b_ml.ml_stack_size + STACK_INCR);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004428 if (newstack == NULL)
4429 return -1;
Bram Moolenaarfbd302f2015-08-08 18:23:46 +02004430 if (top > 0)
4431 mch_memmove(newstack, buf->b_ml.ml_stack,
Bram Moolenaar8c8de832008-06-24 22:58:06 +00004432 (size_t)top * sizeof(infoptr_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004433 vim_free(buf->b_ml.ml_stack);
4434 buf->b_ml.ml_stack = newstack;
4435 buf->b_ml.ml_stack_size += STACK_INCR;
4436 }
4437
4438 buf->b_ml.ml_stack_top++;
4439 return top;
4440}
4441
4442/*
4443 * Update the pointer blocks on the stack for inserted/deleted lines.
4444 * The stack itself is also updated.
4445 *
4446 * When a insert/delete line action fails, the line is not inserted/deleted,
4447 * but the pointer blocks have already been updated. That is fixed here by
4448 * walking through the stack.
4449 *
4450 * Count is the number of lines added, negative if lines have been deleted.
4451 */
4452 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004453ml_lineadd(buf_T *buf, int count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004454{
4455 int idx;
4456 infoptr_T *ip;
4457 PTR_BL *pp;
4458 memfile_T *mfp = buf->b_ml.ml_mfp;
4459 bhdr_T *hp;
4460
4461 for (idx = buf->b_ml.ml_stack_top - 1; idx >= 0; --idx)
4462 {
4463 ip = &(buf->b_ml.ml_stack[idx]);
4464 if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
4465 break;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004466 pp = (PTR_BL *)(hp->bh_data); // must be pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00004467 if (pp->pb_id != PTR_ID)
4468 {
4469 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaareaaac012022-01-02 17:00:40 +00004470 iemsg(_(e_pointer_block_id_wrong_two));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004471 break;
4472 }
4473 pp->pb_pointer[ip->ip_index].pe_line_count += count;
4474 ip->ip_high += count;
4475 mf_put(mfp, hp, TRUE, FALSE);
4476 }
4477}
4478
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004479#if defined(HAVE_READLINK) || defined(PROTO)
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004480/*
4481 * Resolve a symlink in the last component of a file name.
4482 * Note that f_resolve() does it for every part of the path, we don't do that
4483 * here.
4484 * If it worked returns OK and the resolved link in "buf[MAXPATHL]".
4485 * Otherwise returns FAIL.
4486 */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004487 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004488resolve_symlink(char_u *fname, char_u *buf)
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004489{
4490 char_u tmp[MAXPATHL];
4491 int ret;
4492 int depth = 0;
4493
4494 if (fname == NULL)
4495 return FAIL;
4496
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004497 // Put the result so far in tmp[], starting with the original name.
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004498 vim_strncpy(tmp, fname, MAXPATHL - 1);
4499
4500 for (;;)
4501 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004502 // Limit symlink depth to 100, catch recursive loops.
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004503 if (++depth == 100)
4504 {
Bram Moolenaar677658a2022-01-05 16:09:06 +00004505 semsg(_(e_symlink_loop_for_str), fname);
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004506 return FAIL;
4507 }
4508
4509 ret = readlink((char *)tmp, (char *)buf, MAXPATHL - 1);
4510 if (ret <= 0)
4511 {
Bram Moolenaarcc984262005-12-23 22:19:46 +00004512 if (errno == EINVAL || errno == ENOENT)
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004513 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004514 // Found non-symlink or not existing file, stop here.
4515 // When at the first level use the unmodified name, skip the
4516 // call to vim_FullName().
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004517 if (depth == 1)
4518 return FAIL;
4519
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004520 // Use the resolved name in tmp[].
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004521 break;
4522 }
4523
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004524 // There must be some error reading links, use original name.
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004525 return FAIL;
4526 }
4527 buf[ret] = NUL;
4528
4529 /*
4530 * Check whether the symlink is relative or absolute.
4531 * If it's relative, build a new path based on the directory
4532 * portion of the filename (if any) and the path the symlink
4533 * points to.
4534 */
4535 if (mch_isFullName(buf))
4536 STRCPY(tmp, buf);
4537 else
4538 {
4539 char_u *tail;
4540
4541 tail = gettail(tmp);
4542 if (STRLEN(tail) + STRLEN(buf) >= MAXPATHL)
4543 return FAIL;
4544 STRCPY(tail, buf);
4545 }
4546 }
4547
4548 /*
4549 * Try to resolve the full name of the file so that the swapfile name will
4550 * be consistent even when opening a relative symlink from different
4551 * working directories.
4552 */
4553 return vim_FullName(tmp, buf, MAXPATHL, TRUE);
4554}
4555#endif
4556
Bram Moolenaar071d4272004-06-13 20:20:40 +00004557/*
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004558 * Make swap file name out of the file name and a directory name.
4559 * Returns pointer to allocated memory or NULL.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004560 */
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004561 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004562makeswapname(
4563 char_u *fname,
4564 char_u *ffname UNUSED,
4565 buf_T *buf,
4566 char_u *dir_name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004567{
4568 char_u *r, *s;
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02004569 char_u *fname_res = fname;
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004570#ifdef HAVE_READLINK
4571 char_u fname_buf[MAXPATHL];
Bram Moolenaar5966ea12020-07-15 15:30:05 +02004572
4573 // Expand symlink in the file name, so that we put the swap file with the
4574 // actual file instead of with the symlink.
4575 if (resolve_symlink(fname, fname_buf) == OK)
4576 fname_res = fname_buf;
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004577#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004578
Bram Moolenaar4f974752019-02-17 17:44:42 +01004579#if defined(UNIX) || defined(MSWIN) // Need _very_ long file names
Bram Moolenaarb113c3a2017-02-28 21:26:17 +01004580 int len = (int)STRLEN(dir_name);
Bram Moolenaarc525e3a2017-02-18 16:59:02 +01004581
4582 s = dir_name + len;
4583 if (after_pathsep(dir_name, s) && len > 1 && s[-1] == s[-2])
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004584 { // Ends with '//', Use Full path
Bram Moolenaar071d4272004-06-13 20:20:40 +00004585 r = NULL;
Bram Moolenaar5966ea12020-07-15 15:30:05 +02004586 if ((s = make_percent_swname(dir_name, fname_res)) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004587 {
4588 r = modname(s, (char_u *)".swp", FALSE);
4589 vim_free(s);
4590 }
4591 return r;
4592 }
4593#endif
4594
4595 r = buf_modname(
Bram Moolenaar071d4272004-06-13 20:20:40 +00004596 (buf->b_p_sn || buf->b_shortname),
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004597 fname_res,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004598 (char_u *)
Bram Moolenaare60acc12011-05-10 16:41:25 +02004599#if defined(VMS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004600 "_swp",
4601#else
4602 ".swp",
4603#endif
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004604 // Prepend a '.' to the swap file name for the current directory.
Bram Moolenaar48e330a2016-02-23 14:53:34 +01004605 dir_name[0] == '.' && dir_name[1] == NUL);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004606 if (r == NULL) // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00004607 return NULL;
4608
4609 s = get_file_in_dir(r, dir_name);
4610 vim_free(r);
4611 return s;
4612}
4613
4614/*
4615 * Get file name to use for swap file or backup file.
4616 * Use the name of the edited file "fname" and an entry in the 'dir' or 'bdir'
4617 * option "dname".
4618 * - If "dname" is ".", return "fname" (swap file in dir of file).
4619 * - If "dname" starts with "./", insert "dname" in "fname" (swap file
4620 * relative to dir of file).
4621 * - Otherwise, prepend "dname" to the tail of "fname" (swap file in specific
4622 * dir).
4623 *
4624 * The return value is an allocated string and can be NULL.
4625 */
4626 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004627get_file_in_dir(
4628 char_u *fname,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004629 char_u *dname) // don't use "dirname", it is a global for Alpha
Bram Moolenaar071d4272004-06-13 20:20:40 +00004630{
4631 char_u *t;
4632 char_u *tail;
4633 char_u *retval;
4634 int save_char;
4635
4636 tail = gettail(fname);
4637
4638 if (dname[0] == '.' && dname[1] == NUL)
4639 retval = vim_strsave(fname);
4640 else if (dname[0] == '.' && vim_ispathsep(dname[1]))
4641 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004642 if (tail == fname) // no path before file name
Bram Moolenaar071d4272004-06-13 20:20:40 +00004643 retval = concat_fnames(dname + 2, tail, TRUE);
4644 else
4645 {
4646 save_char = *tail;
4647 *tail = NUL;
4648 t = concat_fnames(fname, dname + 2, TRUE);
4649 *tail = save_char;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004650 if (t == NULL) // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00004651 retval = NULL;
4652 else
4653 {
4654 retval = concat_fnames(t, tail, TRUE);
4655 vim_free(t);
4656 }
4657 }
4658 }
4659 else
4660 retval = concat_fnames(dname, tail, TRUE);
4661
Bram Moolenaar4f974752019-02-17 17:44:42 +01004662#ifdef MSWIN
Bram Moolenaar69c35002013-11-04 02:54:12 +01004663 if (retval != NULL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01004664 for (t = gettail(retval); *t != NUL; MB_PTR_ADV(t))
Bram Moolenaar69c35002013-11-04 02:54:12 +01004665 if (*t == ':')
4666 *t = '%';
4667#endif
4668
Bram Moolenaar071d4272004-06-13 20:20:40 +00004669 return retval;
4670}
4671
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004672/*
4673 * Print the ATTENTION message: info about an existing swap file.
4674 */
4675 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004676attention_message(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004677 buf_T *buf, // buffer being edited
4678 char_u *fname) // swap file name
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004679{
Bram Moolenaar8767f522016-07-01 17:17:39 +02004680 stat_T st;
Bram Moolenaar63d25552019-05-10 21:28:38 +02004681 time_t swap_mtime;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004682
4683 ++no_wait_return;
Bram Moolenaareaaac012022-01-02 17:00:40 +00004684 (void)emsg(_(e_attention));
Bram Moolenaar32526b32019-01-19 17:43:09 +01004685 msg_puts(_("\nFound a swap file by the name \""));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004686 msg_home_replace(fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004687 msg_puts("\"\n");
Bram Moolenaar63d25552019-05-10 21:28:38 +02004688 swap_mtime = swapfile_info(fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004689 msg_puts(_("While opening file \""));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004690 msg_outtrans(buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004691 msg_puts("\"\n");
Bram Moolenaard6105cb2018-10-13 19:06:27 +02004692 if (mch_stat((char *)buf->b_fname, &st) == -1)
4693 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004694 msg_puts(_(" CANNOT BE FOUND"));
Bram Moolenaard6105cb2018-10-13 19:06:27 +02004695 }
4696 else
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004697 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004698 msg_puts(_(" dated: "));
Bram Moolenaar63d25552019-05-10 21:28:38 +02004699 msg_puts(get_ctime(st.st_mtime, TRUE));
4700 if (swap_mtime != 0 && st.st_mtime > swap_mtime)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004701 msg_puts(_(" NEWER than swap file!\n"));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004702 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004703 // Some of these messages are long to allow translation to
4704 // other languages.
Bram Moolenaar32526b32019-01-19 17:43:09 +01004705 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"));
4706 msg_puts(_("(2) An edit session for this file crashed.\n"));
4707 msg_puts(_(" If this is the case, use \":recover\" or \"vim -r "));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004708 msg_outtrans(buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004709 msg_puts(_("\"\n to recover the changes (see \":help recovery\").\n"));
4710 msg_puts(_(" If you did this already, delete the swap file \""));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004711 msg_outtrans(fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004712 msg_puts(_("\"\n to avoid this message.\n"));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004713 cmdline_row = msg_row;
4714 --no_wait_return;
4715}
4716
Bram Moolenaar188639d2022-04-04 16:57:21 +01004717typedef enum {
4718 SEA_CHOICE_NONE = 0,
4719 SEA_CHOICE_READONLY = 1,
4720 SEA_CHOICE_EDIT = 2,
4721 SEA_CHOICE_RECOVER = 3,
4722 SEA_CHOICE_DELETE = 4,
4723 SEA_CHOICE_QUIT = 5,
4724 SEA_CHOICE_ABORT = 6
4725} sea_choice_T;
4726
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01004727#if defined(FEAT_EVAL)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004728/*
4729 * Trigger the SwapExists autocommands.
Bram Moolenaar188639d2022-04-04 16:57:21 +01004730 * Returns a value for equivalent to do_dialog().
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004731 */
Bram Moolenaar188639d2022-04-04 16:57:21 +01004732 static sea_choice_T
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004733do_swapexists(buf_T *buf, char_u *fname)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004734{
4735 set_vim_var_string(VV_SWAPNAME, fname, -1);
4736 set_vim_var_string(VV_SWAPCHOICE, NULL, -1);
4737
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004738 // Trigger SwapExists autocommands with <afile> set to the file being
4739 // edited. Disallow changing directory here.
Bram Moolenaar12c22ce2009-04-22 13:58:46 +00004740 ++allbuf_lock;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004741 apply_autocmds(EVENT_SWAPEXISTS, buf->b_fname, NULL, FALSE, NULL);
Bram Moolenaar12c22ce2009-04-22 13:58:46 +00004742 --allbuf_lock;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004743
4744 set_vim_var_string(VV_SWAPNAME, NULL, -1);
4745
4746 switch (*get_vim_var_str(VV_SWAPCHOICE))
4747 {
Bram Moolenaar188639d2022-04-04 16:57:21 +01004748 case 'o': return SEA_CHOICE_READONLY;
4749 case 'e': return SEA_CHOICE_EDIT;
4750 case 'r': return SEA_CHOICE_RECOVER;
4751 case 'd': return SEA_CHOICE_DELETE;
4752 case 'q': return SEA_CHOICE_QUIT;
4753 case 'a': return SEA_CHOICE_ABORT;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004754 }
4755
Bram Moolenaar188639d2022-04-04 16:57:21 +01004756 return SEA_CHOICE_NONE;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004757}
4758#endif
4759
Bram Moolenaar071d4272004-06-13 20:20:40 +00004760/*
4761 * Find out what name to use for the swap file for buffer 'buf'.
4762 *
4763 * Several names are tried to find one that does not exist
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004764 * Returns the name in allocated memory or NULL.
Bram Moolenaarf541c362011-10-26 11:44:18 +02004765 * When out of memory "dirp" is set to NULL.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004766 *
4767 * Note: If BASENAMELEN is not correct, you will get error messages for
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004768 * not being able to open the swap or undo file
Bram Moolenaar12c22ce2009-04-22 13:58:46 +00004769 * Note: May trigger SwapExists autocmd, pointers may change!
Bram Moolenaar071d4272004-06-13 20:20:40 +00004770 */
4771 static char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004772findswapname(
4773 buf_T *buf,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004774 char_u **dirp, // pointer to list of directories
4775 char_u *old_fname) // don't give warning for this file name
Bram Moolenaar071d4272004-06-13 20:20:40 +00004776{
4777 char_u *fname;
4778 int n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004779 char_u *dir_name;
4780#ifdef AMIGA
4781 BPTR fh;
4782#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004783 int r;
Bram Moolenaar69c35002013-11-04 02:54:12 +01004784 char_u *buf_fname = buf->b_fname;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004785
Bram Moolenaar48e330a2016-02-23 14:53:34 +01004786#if !defined(UNIX)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004787# define CREATE_DUMMY_FILE
4788 FILE *dummyfd = NULL;
4789
Bram Moolenaar4f974752019-02-17 17:44:42 +01004790# ifdef MSWIN
Bram Moolenaar69c35002013-11-04 02:54:12 +01004791 if (buf_fname != NULL && !mch_isFullName(buf_fname)
4792 && vim_strchr(gettail(buf_fname), ':'))
4793 {
4794 char_u *t;
4795
4796 buf_fname = vim_strsave(buf_fname);
4797 if (buf_fname == NULL)
4798 buf_fname = buf->b_fname;
4799 else
Bram Moolenaar91acfff2017-03-12 19:22:36 +01004800 for (t = gettail(buf_fname); *t != NUL; MB_PTR_ADV(t))
Bram Moolenaar69c35002013-11-04 02:54:12 +01004801 if (*t == ':')
4802 *t = '%';
4803 }
4804# endif
4805
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004806 /*
4807 * If we start editing a new file, e.g. "test.doc", which resides on an
4808 * MSDOS compatible filesystem, it is possible that the file
4809 * "test.doc.swp" which we create will be exactly the same file. To avoid
4810 * this problem we temporarily create "test.doc". Don't do this when the
Dominique Pelleaf4a61a2021-12-27 17:21:41 +00004811 * check below for an 8.3 file name is used.
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004812 */
Bram Moolenaar69c35002013-11-04 02:54:12 +01004813 if (!(buf->b_p_sn || buf->b_shortname) && buf_fname != NULL
4814 && mch_getperm(buf_fname) < 0)
4815 dummyfd = mch_fopen((char *)buf_fname, "w");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004816#endif
4817
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004818 /*
4819 * Isolate a directory name from *dirp and put it in dir_name.
4820 * First allocate some memory to put the directory name in.
4821 */
Bram Moolenaar964b3742019-05-24 18:54:09 +02004822 dir_name = alloc(STRLEN(*dirp) + 1);
Bram Moolenaarf541c362011-10-26 11:44:18 +02004823 if (dir_name == NULL)
4824 *dirp = NULL;
4825 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004826 (void)copy_option_part(dirp, dir_name, 31000, ",");
4827
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004828 /*
4829 * we try different names until we find one that does not exist yet
4830 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004831 if (dir_name == NULL) // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00004832 fname = NULL;
4833 else
Bram Moolenaar69c35002013-11-04 02:54:12 +01004834 fname = makeswapname(buf_fname, buf->b_ffname, buf, dir_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004835
4836 for (;;)
4837 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004838 if (fname == NULL) // must be out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00004839 break;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004840 if ((n = (int)STRLEN(fname)) == 0) // safety check
Bram Moolenaar071d4272004-06-13 20:20:40 +00004841 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01004842 VIM_CLEAR(fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004843 break;
4844 }
Bram Moolenaar48e330a2016-02-23 14:53:34 +01004845#if defined(UNIX)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004846/*
4847 * Some systems have a MS-DOS compatible filesystem that use 8.3 character
4848 * file names. If this is the first try and the swap file name does not fit in
4849 * 8.3, detect if this is the case, set shortname and try again.
4850 */
4851 if (fname[n - 2] == 'w' && fname[n - 1] == 'p'
4852 && !(buf->b_p_sn || buf->b_shortname))
4853 {
4854 char_u *tail;
4855 char_u *fname2;
Bram Moolenaar8767f522016-07-01 17:17:39 +02004856 stat_T s1, s2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004857 int f1, f2;
4858 int created1 = FALSE, created2 = FALSE;
4859 int same = FALSE;
4860
4861 /*
4862 * Check if swapfile name does not fit in 8.3:
4863 * It either contains two dots, is longer than 8 chars, or starts
4864 * with a dot.
4865 */
Bram Moolenaar69c35002013-11-04 02:54:12 +01004866 tail = gettail(buf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004867 if ( vim_strchr(tail, '.') != NULL
4868 || STRLEN(tail) > (size_t)8
4869 || *gettail(fname) == '.')
4870 {
4871 fname2 = alloc(n + 2);
4872 if (fname2 != NULL)
4873 {
4874 STRCPY(fname2, fname);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004875 // if fname == "xx.xx.swp", fname2 = "xx.xx.swx"
4876 // if fname == ".xx.swp", fname2 = ".xx.swpx"
4877 // if fname == "123456789.swp", fname2 = "12345678x.swp"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004878 if (vim_strchr(tail, '.') != NULL)
4879 fname2[n - 1] = 'x';
4880 else if (*gettail(fname) == '.')
4881 {
4882 fname2[n] = 'x';
4883 fname2[n + 1] = NUL;
4884 }
4885 else
4886 fname2[n - 5] += 1;
4887 /*
4888 * may need to create the files to be able to use mch_stat()
4889 */
4890 f1 = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
4891 if (f1 < 0)
4892 {
4893 f1 = mch_open_rw((char *)fname,
4894 O_RDWR|O_CREAT|O_EXCL|O_EXTRA);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004895 created1 = TRUE;
4896 }
4897 if (f1 >= 0)
4898 {
4899 f2 = mch_open((char *)fname2, O_RDONLY | O_EXTRA, 0);
4900 if (f2 < 0)
4901 {
4902 f2 = mch_open_rw((char *)fname2,
4903 O_RDWR|O_CREAT|O_EXCL|O_EXTRA);
4904 created2 = TRUE;
4905 }
4906 if (f2 >= 0)
4907 {
4908 /*
4909 * Both files exist now. If mch_stat() returns the
4910 * same device and inode they are the same file.
4911 */
4912 if (mch_fstat(f1, &s1) != -1
4913 && mch_fstat(f2, &s2) != -1
4914 && s1.st_dev == s2.st_dev
4915 && s1.st_ino == s2.st_ino)
4916 same = TRUE;
4917 close(f2);
4918 if (created2)
4919 mch_remove(fname2);
4920 }
4921 close(f1);
4922 if (created1)
4923 mch_remove(fname);
4924 }
4925 vim_free(fname2);
4926 if (same)
4927 {
4928 buf->b_shortname = TRUE;
4929 vim_free(fname);
Bram Moolenaar69c35002013-11-04 02:54:12 +01004930 fname = makeswapname(buf_fname, buf->b_ffname,
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004931 buf, dir_name);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004932 continue; // try again with b_shortname set
Bram Moolenaar071d4272004-06-13 20:20:40 +00004933 }
4934 }
4935 }
4936 }
4937#endif
4938 /*
4939 * check if the swapfile already exists
4940 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004941 if (mch_getperm(fname) < 0) // it does not exist
Bram Moolenaar071d4272004-06-13 20:20:40 +00004942 {
4943#ifdef HAVE_LSTAT
Bram Moolenaar8767f522016-07-01 17:17:39 +02004944 stat_T sb;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004945
4946 /*
4947 * Extra security check: When a swap file is a symbolic link, this
4948 * is most likely a symlink attack.
4949 */
4950 if (mch_lstat((char *)fname, &sb) < 0)
4951#else
4952# ifdef AMIGA
4953 fh = Open((UBYTE *)fname, (long)MODE_NEWFILE);
4954 /*
4955 * on the Amiga mch_getperm() will return -1 when the file exists
4956 * but is being used by another program. This happens if you edit
4957 * a file twice.
4958 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004959 if (fh != (BPTR)NULL) // can open file, OK
Bram Moolenaar071d4272004-06-13 20:20:40 +00004960 {
4961 Close(fh);
4962 mch_remove(fname);
4963 break;
4964 }
4965 if (IoErr() != ERROR_OBJECT_IN_USE
4966 && IoErr() != ERROR_OBJECT_EXISTS)
4967# endif
4968#endif
4969 break;
4970 }
4971
4972 /*
4973 * A file name equal to old_fname is OK to use.
4974 */
4975 if (old_fname != NULL && fnamecmp(fname, old_fname) == 0)
4976 break;
4977
4978 /*
4979 * get here when file already exists
4980 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004981 if (fname[n - 2] == 'w' && fname[n - 1] == 'p') // first try
Bram Moolenaar071d4272004-06-13 20:20:40 +00004982 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00004983 /*
4984 * on MS-DOS compatible filesystems (e.g. messydos) file.doc.swp
4985 * and file.doc are the same file. To guess if this problem is
4986 * present try if file.doc.swx exists. If it does, we set
4987 * buf->b_shortname and try file_doc.swp (dots replaced by
4988 * underscores for this file), and try again. If it doesn't we
4989 * assume that "file.doc.swp" already exists.
4990 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004991 if (!(buf->b_p_sn || buf->b_shortname)) // not tried yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00004992 {
4993 fname[n - 1] = 'x';
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004994 r = mch_getperm(fname); // try "file.swx"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004995 fname[n - 1] = 'p';
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004996 if (r >= 0) // "file.swx" seems to exist
Bram Moolenaar071d4272004-06-13 20:20:40 +00004997 {
4998 buf->b_shortname = TRUE;
4999 vim_free(fname);
Bram Moolenaar69c35002013-11-04 02:54:12 +01005000 fname = makeswapname(buf_fname, buf->b_ffname,
Bram Moolenaar04a09c12005-08-01 22:02:32 +00005001 buf, dir_name);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005002 continue; // try again with '.' replaced with '_'
Bram Moolenaar071d4272004-06-13 20:20:40 +00005003 }
5004 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005005 /*
5006 * If we get here the ".swp" file really exists.
5007 * Give an error message, unless recovering, no file name, we are
5008 * viewing a help file or when the path of the file is different
5009 * (happens when all .swp files are in one directory).
5010 */
Bram Moolenaar69c35002013-11-04 02:54:12 +01005011 if (!recoverymode && buf_fname != NULL
Bram Moolenaar2debf1c2019-08-04 20:44:19 +02005012 && !buf->b_help
5013 && !(buf->b_flags & (BF_DUMMY | BF_NO_SEA)))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005014 {
5015 int fd;
5016 struct block0 b0;
5017 int differ = FALSE;
5018
5019 /*
5020 * Try to read block 0 from the swap file to get the original
5021 * file name (and inode number).
5022 */
5023 fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
5024 if (fd >= 0)
5025 {
Bram Moolenaar540fc6f2010-12-17 16:27:16 +01005026 if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005027 {
5028 /*
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005029 * If the swapfile has the same directory as the
5030 * buffer don't compare the directory names, they can
5031 * have a different mountpoint.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005032 */
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005033 if (b0.b0_flags & B0_SAME_DIR)
5034 {
5035 if (fnamecmp(gettail(buf->b_ffname),
5036 gettail(b0.b0_fname)) != 0
5037 || !same_directory(fname, buf->b_ffname))
Bram Moolenaar900b4d72005-12-12 22:05:50 +00005038 {
5039#ifdef CHECK_INODE
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005040 // Symlinks may point to the same file even
5041 // when the name differs, need to check the
5042 // inode too.
Bram Moolenaar900b4d72005-12-12 22:05:50 +00005043 expand_env(b0.b0_fname, NameBuff, MAXPATHL);
5044 if (fnamecmp_ino(buf->b_ffname, NameBuff,
5045 char_to_long(b0.b0_ino)))
5046#endif
5047 differ = TRUE;
5048 }
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005049 }
5050 else
5051 {
5052 /*
5053 * The name in the swap file may be
5054 * "~user/path/file". Expand it first.
5055 */
5056 expand_env(b0.b0_fname, NameBuff, MAXPATHL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005057#ifdef CHECK_INODE
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005058 if (fnamecmp_ino(buf->b_ffname, NameBuff,
Bram Moolenaar900b4d72005-12-12 22:05:50 +00005059 char_to_long(b0.b0_ino)))
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005060 differ = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005061#else
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005062 if (fnamecmp(NameBuff, buf->b_ffname) != 0)
5063 differ = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005064#endif
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005065 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005066 }
5067 close(fd);
5068 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005069
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005070 // give the ATTENTION message when there is an old swap file
5071 // for the current file, and the buffer was not recovered.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005072 if (differ == FALSE && !(curbuf->b_flags & BF_RECOVERED)
5073 && vim_strchr(p_shm, SHM_ATTENTION) == NULL)
5074 {
Bram Moolenaar188639d2022-04-04 16:57:21 +01005075 sea_choice_T choice = SEA_CHOICE_NONE;
5076 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005077#ifdef CREATE_DUMMY_FILE
Bram Moolenaar188639d2022-04-04 16:57:21 +01005078 int did_use_dummy = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005079
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005080 // Avoid getting a warning for the file being created
5081 // outside of Vim, it was created at the start of this
5082 // function. Delete the file now, because Vim might exit
5083 // here if the window is closed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005084 if (dummyfd != NULL)
5085 {
5086 fclose(dummyfd);
5087 dummyfd = NULL;
Bram Moolenaar69c35002013-11-04 02:54:12 +01005088 mch_remove(buf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005089 did_use_dummy = TRUE;
5090 }
5091#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005092
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02005093#ifdef HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar071d4272004-06-13 20:20:40 +00005094 process_still_running = FALSE;
5095#endif
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02005096 // It's safe to delete the swap file if all these are true:
5097 // - the edited file exists
5098 // - the swap file has no changes and looks OK
5099 if (mch_stat((char *)buf->b_fname, &st) == 0
5100 && swapfile_unchanged(fname))
5101 {
Bram Moolenaar188639d2022-04-04 16:57:21 +01005102 choice = SEA_CHOICE_DELETE;
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02005103 if (p_verbose > 0)
5104 verb_msg(_("Found a swap file that is not useful, deleting it"));
5105 }
5106
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01005107#if defined(FEAT_EVAL)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005108 /*
5109 * If there is an SwapExists autocommand and we can handle
5110 * the response, trigger it. It may return 0 to ask the
5111 * user anyway.
5112 */
Bram Moolenaar188639d2022-04-04 16:57:21 +01005113 if (choice == SEA_CHOICE_NONE
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02005114 && swap_exists_action != SEA_NONE
Bram Moolenaar69c35002013-11-04 02:54:12 +01005115 && has_autocmd(EVENT_SWAPEXISTS, buf_fname, buf))
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005116 choice = do_swapexists(buf, fname);
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005117#endif
Bram Moolenaar188639d2022-04-04 16:57:21 +01005118
5119 if (choice == SEA_CHOICE_NONE
5120 && swap_exists_action == SEA_READONLY)
5121 {
5122 // always open readonly.
5123 choice = SEA_CHOICE_READONLY;
5124 }
5125
5126 if (choice == SEA_CHOICE_NONE)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005127 {
5128#ifdef FEAT_GUI
Bram Moolenaar798184c2018-10-07 20:48:39 +02005129 // If we are supposed to start the GUI but it wasn't
5130 // completely started yet, start it now. This makes
5131 // the messages displayed in the Vim window when
5132 // loading a session from the .gvimrc file.
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005133 if (gui.starting && !gui.in_use)
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005134 gui_start(NULL);
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005135#endif
Bram Moolenaar798184c2018-10-07 20:48:39 +02005136 // Show info about the existing swap file.
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005137 attention_message(buf, fname);
5138
Bram Moolenaar798184c2018-10-07 20:48:39 +02005139 // We don't want a 'q' typed at the more-prompt
5140 // interrupt loading a file.
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005141 got_int = FALSE;
Bram Moolenaar798184c2018-10-07 20:48:39 +02005142
5143 // If vimrc has "simalt ~x" we don't want it to
5144 // interfere with the prompt here.
Bram Moolenaar6a2633b2018-10-07 23:16:36 +02005145 flush_buffers(FLUSH_TYPEAHEAD);
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005146 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005147
5148#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
Bram Moolenaar188639d2022-04-04 16:57:21 +01005149 if (swap_exists_action != SEA_NONE
5150 && choice == SEA_CHOICE_NONE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005151 {
5152 char_u *name;
Bram Moolenaar188639d2022-04-04 16:57:21 +01005153 int dialog_result;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005154
Bram Moolenaar964b3742019-05-24 18:54:09 +02005155 name = alloc(STRLEN(fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005156 + STRLEN(_("Swap file \""))
Bram Moolenaar964b3742019-05-24 18:54:09 +02005157 + STRLEN(_("\" already exists!")) + 5);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005158 if (name != NULL)
5159 {
5160 STRCPY(name, _("Swap file \""));
5161 home_replace(NULL, fname, name + STRLEN(name),
5162 1000, TRUE);
5163 STRCAT(name, _("\" already exists!"));
5164 }
Bram Moolenaar188639d2022-04-04 16:57:21 +01005165 dialog_result = do_dialog(VIM_WARNING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005166 (char_u *)_("VIM - ATTENTION"),
5167 name == NULL
5168 ? (char_u *)_("Swap file already exists!")
5169 : name,
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02005170# ifdef HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar071d4272004-06-13 20:20:40 +00005171 process_still_running
5172 ? (char_u *)_("&Open Read-Only\n&Edit anyway\n&Recover\n&Quit\n&Abort") :
5173# endif
Bram Moolenaard2c340a2011-01-17 20:08:11 +01005174 (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 +00005175
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02005176# ifdef HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar188639d2022-04-04 16:57:21 +01005177 if (process_still_running && dialog_result >= 4)
5178 // compensate for missing "Delete it" button
5179 dialog_result++;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005180# endif
Bram Moolenaar188639d2022-04-04 16:57:21 +01005181 choice = dialog_result;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005182 vim_free(name);
5183
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005184 // pretend screen didn't scroll, need redraw anyway
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005185 msg_scrolled = 0;
Bram Moolenaara4d158b2022-08-14 14:17:45 +01005186 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005187 }
5188#endif
5189
Bram Moolenaar188639d2022-04-04 16:57:21 +01005190 switch (choice)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005191 {
Bram Moolenaar188639d2022-04-04 16:57:21 +01005192 case SEA_CHOICE_READONLY:
5193 buf->b_p_ro = TRUE;
5194 break;
5195 case SEA_CHOICE_EDIT:
5196 break;
5197 case SEA_CHOICE_RECOVER:
5198 swap_exists_action = SEA_RECOVER;
5199 break;
5200 case SEA_CHOICE_DELETE:
5201 mch_remove(fname);
5202 break;
5203 case SEA_CHOICE_QUIT:
5204 swap_exists_action = SEA_QUIT;
5205 break;
5206 case SEA_CHOICE_ABORT:
5207 swap_exists_action = SEA_QUIT;
5208 got_int = TRUE;
5209 break;
5210 case SEA_CHOICE_NONE:
5211 msg_puts("\n");
5212 if (msg_silent == 0)
5213 // call wait_return() later
5214 need_wait_return = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005215 break;
5216 }
Bram Moolenaar188639d2022-04-04 16:57:21 +01005217
5218 // If the file was deleted this fname can be used.
5219 if (choice != SEA_CHOICE_NONE && mch_getperm(fname) < 0)
5220 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005221
5222#ifdef CREATE_DUMMY_FILE
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005223 // Going to try another name, need the dummy file again.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005224 if (did_use_dummy)
Bram Moolenaar69c35002013-11-04 02:54:12 +01005225 dummyfd = mch_fopen((char *)buf_fname, "w");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005226#endif
5227 }
5228 }
5229 }
5230
5231 /*
5232 * Change the ".swp" extension to find another file that can be used.
5233 * First decrement the last char: ".swo", ".swn", etc.
5234 * If that still isn't enough decrement the last but one char: ".svz"
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00005235 * Can happen when editing many "No Name" buffers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005236 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005237 if (fname[n - 1] == 'a') // ".s?a"
Bram Moolenaar071d4272004-06-13 20:20:40 +00005238 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005239 if (fname[n - 2] == 'a') // ".saa": tried enough, give up
Bram Moolenaar071d4272004-06-13 20:20:40 +00005240 {
Bram Moolenaareaaac012022-01-02 17:00:40 +00005241 emsg(_(e_too_many_swap_files_found));
Bram Moolenaard23a8232018-02-10 18:45:26 +01005242 VIM_CLEAR(fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005243 break;
5244 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005245 --fname[n - 2]; // ".svz", ".suz", etc.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005246 fname[n - 1] = 'z' + 1;
5247 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005248 --fname[n - 1]; // ".swo", ".swn", etc.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005249 }
5250
5251 vim_free(dir_name);
5252#ifdef CREATE_DUMMY_FILE
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005253 if (dummyfd != NULL) // file has been created temporarily
Bram Moolenaar071d4272004-06-13 20:20:40 +00005254 {
5255 fclose(dummyfd);
Bram Moolenaar69c35002013-11-04 02:54:12 +01005256 mch_remove(buf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005257 }
5258#endif
Bram Moolenaar4f974752019-02-17 17:44:42 +01005259#ifdef MSWIN
Bram Moolenaar69c35002013-11-04 02:54:12 +01005260 if (buf_fname != buf->b_fname)
5261 vim_free(buf_fname);
5262#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005263 return fname;
5264}
5265
5266 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005267b0_magic_wrong(ZERO_BL *b0p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005268{
5269 return (b0p->b0_magic_long != (long)B0_MAGIC_LONG
5270 || b0p->b0_magic_int != (int)B0_MAGIC_INT
5271 || b0p->b0_magic_short != (short)B0_MAGIC_SHORT
5272 || b0p->b0_magic_char != B0_MAGIC_CHAR);
5273}
5274
5275#ifdef CHECK_INODE
5276/*
5277 * Compare current file name with file name from swap file.
5278 * Try to use inode numbers when possible.
5279 * Return non-zero when files are different.
5280 *
5281 * When comparing file names a few things have to be taken into consideration:
5282 * - When working over a network the full path of a file depends on the host.
5283 * We check the inode number if possible. It is not 100% reliable though,
5284 * because the device number cannot be used over a network.
5285 * - When a file does not exist yet (editing a new file) there is no inode
5286 * number.
5287 * - The file name in a swap file may not be valid on the current host. The
5288 * "~user" form is used whenever possible to avoid this.
5289 *
5290 * This is getting complicated, let's make a table:
5291 *
5292 * ino_c ino_s fname_c fname_s differ =
5293 *
5294 * both files exist -> compare inode numbers:
5295 * != 0 != 0 X X ino_c != ino_s
5296 *
5297 * inode number(s) unknown, file names available -> compare file names
5298 * == 0 X OK OK fname_c != fname_s
5299 * X == 0 OK OK fname_c != fname_s
5300 *
5301 * current file doesn't exist, file for swap file exist, file name(s) not
5302 * available -> probably different
5303 * == 0 != 0 FAIL X TRUE
5304 * == 0 != 0 X FAIL TRUE
5305 *
5306 * current file exists, inode for swap unknown, file name(s) not
5307 * available -> probably different
5308 * != 0 == 0 FAIL X TRUE
5309 * != 0 == 0 X FAIL TRUE
5310 *
5311 * current file doesn't exist, inode for swap unknown, one file name not
5312 * available -> probably different
5313 * == 0 == 0 FAIL OK TRUE
5314 * == 0 == 0 OK FAIL TRUE
5315 *
5316 * current file doesn't exist, inode for swap unknown, both file names not
Bram Moolenaar8c3169c2018-05-12 17:04:12 +02005317 * available -> compare file names
5318 * == 0 == 0 FAIL FAIL fname_c != fname_s
Bram Moolenaar071d4272004-06-13 20:20:40 +00005319 *
5320 * Note that when the ino_t is 64 bits, only the last 32 will be used. This
5321 * can't be changed without making the block 0 incompatible with 32 bit
5322 * versions.
5323 */
5324
5325 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005326fnamecmp_ino(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005327 char_u *fname_c, // current file name
5328 char_u *fname_s, // file name from swap file
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005329 long ino_block0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005330{
Bram Moolenaar8767f522016-07-01 17:17:39 +02005331 stat_T st;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005332 ino_t ino_c = 0; // ino of current file
5333 ino_t ino_s; // ino of file from swap file
5334 char_u buf_c[MAXPATHL]; // full path of fname_c
5335 char_u buf_s[MAXPATHL]; // full path of fname_s
5336 int retval_c; // flag: buf_c valid
5337 int retval_s; // flag: buf_s valid
Bram Moolenaar071d4272004-06-13 20:20:40 +00005338
5339 if (mch_stat((char *)fname_c, &st) == 0)
5340 ino_c = (ino_t)st.st_ino;
5341
5342 /*
5343 * First we try to get the inode from the file name, because the inode in
5344 * the swap file may be outdated. If that fails (e.g. this path is not
5345 * valid on this machine), use the inode from block 0.
5346 */
5347 if (mch_stat((char *)fname_s, &st) == 0)
5348 ino_s = (ino_t)st.st_ino;
5349 else
5350 ino_s = (ino_t)ino_block0;
5351
5352 if (ino_c && ino_s)
5353 return (ino_c != ino_s);
5354
5355 /*
5356 * One of the inode numbers is unknown, try a forced vim_FullName() and
5357 * compare the file names.
5358 */
5359 retval_c = vim_FullName(fname_c, buf_c, MAXPATHL, TRUE);
5360 retval_s = vim_FullName(fname_s, buf_s, MAXPATHL, TRUE);
5361 if (retval_c == OK && retval_s == OK)
Bram Moolenaar8c3169c2018-05-12 17:04:12 +02005362 return STRCMP(buf_c, buf_s) != 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005363
5364 /*
5365 * Can't compare inodes or file names, guess that the files are different,
Bram Moolenaar8c3169c2018-05-12 17:04:12 +02005366 * unless both appear not to exist at all, then compare with the file name
5367 * in the swap file.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005368 */
5369 if (ino_s == 0 && ino_c == 0 && retval_c == FAIL && retval_s == FAIL)
Bram Moolenaar8c3169c2018-05-12 17:04:12 +02005370 return STRCMP(fname_c, fname_s) != 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005371 return TRUE;
5372}
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005373#endif // CHECK_INODE
Bram Moolenaar071d4272004-06-13 20:20:40 +00005374
5375/*
5376 * Move a long integer into a four byte character array.
5377 * Used for machine independency in block zero.
5378 */
5379 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005380long_to_char(long n, char_u *s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005381{
5382 s[0] = (char_u)(n & 0xff);
5383 n = (unsigned)n >> 8;
5384 s[1] = (char_u)(n & 0xff);
5385 n = (unsigned)n >> 8;
5386 s[2] = (char_u)(n & 0xff);
5387 n = (unsigned)n >> 8;
5388 s[3] = (char_u)(n & 0xff);
5389}
5390
5391 static long
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005392char_to_long(char_u *s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005393{
5394 long retval;
5395
5396 retval = s[3];
5397 retval <<= 8;
5398 retval |= s[2];
5399 retval <<= 8;
5400 retval |= s[1];
5401 retval <<= 8;
5402 retval |= s[0];
5403
5404 return retval;
5405}
5406
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005407/*
5408 * Set the flags in the first block of the swap file:
5409 * - file is modified or not: buf->b_changed
5410 * - 'fileformat'
5411 * - 'fileencoding'
5412 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005413 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005414ml_setflags(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005415{
5416 bhdr_T *hp;
5417 ZERO_BL *b0p;
5418
5419 if (!buf->b_ml.ml_mfp)
5420 return;
5421 for (hp = buf->b_ml.ml_mfp->mf_used_last; hp != NULL; hp = hp->bh_prev)
5422 {
5423 if (hp->bh_bnum == 0)
5424 {
5425 b0p = (ZERO_BL *)(hp->bh_data);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005426 b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0;
5427 b0p->b0_flags = (b0p->b0_flags & ~B0_FF_MASK)
5428 | (get_fileformat(buf) + 1);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005429 add_b0_fenc(b0p, buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005430 hp->bh_flags |= BH_DIRTY;
5431 mf_sync(buf->b_ml.ml_mfp, MFS_ZERO);
5432 break;
5433 }
5434 }
5435}
5436
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005437#if defined(FEAT_CRYPT) || defined(PROTO)
5438/*
5439 * If "data" points to a data block encrypt the text in it and return a copy
5440 * in allocated memory. Return NULL when out of memory.
5441 * Otherwise return "data".
5442 */
5443 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005444ml_encrypt_data(
5445 memfile_T *mfp,
5446 char_u *data,
Bram Moolenaar8767f522016-07-01 17:17:39 +02005447 off_T offset,
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005448 unsigned size)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005449{
5450 DATA_BL *dp = (DATA_BL *)data;
5451 char_u *head_end;
5452 char_u *text_start;
5453 char_u *new_data;
5454 int text_len;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005455 cryptstate_T *state;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005456
5457 if (dp->db_id != DATA_ID)
5458 return data;
5459
Bram Moolenaarbc563362015-06-09 18:35:25 +02005460 state = ml_crypt_prepare(mfp, offset, FALSE);
5461 if (state == NULL)
5462 return data;
5463
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005464 new_data = alloc(size);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005465 if (new_data == NULL)
5466 return NULL;
5467 head_end = (char_u *)(&dp->db_index[dp->db_line_count]);
5468 text_start = (char_u *)dp + dp->db_txt_start;
5469 text_len = size - dp->db_txt_start;
5470
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005471 // Copy the header and the text.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005472 mch_memmove(new_data, dp, head_end - (char_u *)dp);
5473
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005474 // Encrypt the text.
Christian Brabandtf573c6e2021-06-20 14:02:16 +02005475 crypt_encode(state, text_start, text_len, new_data + dp->db_txt_start,
5476 FALSE);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005477 crypt_free_state(state);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005478
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005479 // Clear the gap.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005480 if (head_end < text_start)
5481 vim_memset(new_data + (head_end - data), 0, text_start - head_end);
5482
5483 return new_data;
5484}
5485
5486/*
Bram Moolenaarbc563362015-06-09 18:35:25 +02005487 * Decrypt the text in "data" if it points to an encrypted data block.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005488 */
5489 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005490ml_decrypt_data(
5491 memfile_T *mfp,
5492 char_u *data,
Bram Moolenaar8767f522016-07-01 17:17:39 +02005493 off_T offset,
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005494 unsigned size)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005495{
5496 DATA_BL *dp = (DATA_BL *)data;
5497 char_u *head_end;
5498 char_u *text_start;
5499 int text_len;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005500 cryptstate_T *state;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005501
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00005502 if (dp->db_id != DATA_ID)
5503 return;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005504
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00005505 head_end = (char_u *)(&dp->db_index[dp->db_line_count]);
5506 text_start = (char_u *)dp + dp->db_txt_start;
5507 text_len = dp->db_txt_end - dp->db_txt_start;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005508
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00005509 if (head_end > text_start || dp->db_txt_start > size
5510 || dp->db_txt_end > size)
5511 return; // data was messed up
5512
5513 state = ml_crypt_prepare(mfp, offset, TRUE);
5514 if (state == NULL)
5515 return;
5516
5517 // Decrypt the text in place.
5518 crypt_decode_inplace(state, text_start, text_len, FALSE);
5519 crypt_free_state(state);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005520}
5521
5522/*
5523 * Prepare for encryption/decryption, using the key, seed and offset.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005524 * Return an allocated cryptstate_T *.
Christian Brabandtaae58342023-04-23 17:50:22 +01005525 * Note: Encryption not supported for SODIUM
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005526 */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005527 static cryptstate_T *
Bram Moolenaar8767f522016-07-01 17:17:39 +02005528ml_crypt_prepare(memfile_T *mfp, off_T offset, int reading)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005529{
5530 buf_T *buf = mfp->mf_buffer;
5531 char_u salt[50];
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005532 int method_nr;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005533 char_u *key;
Christian Brabandtaae58342023-04-23 17:50:22 +01005534 crypt_arg_T arg;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005535
Christian Brabandtaae58342023-04-23 17:50:22 +01005536 CLEAR_FIELD(arg);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005537 if (reading && mfp->mf_old_key != NULL)
5538 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005539 // Reading back blocks with the previous key/method/seed.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005540 method_nr = mfp->mf_old_cm;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005541 key = mfp->mf_old_key;
Christian Brabandtaae58342023-04-23 17:50:22 +01005542 arg.cat_seed = mfp->mf_old_seed;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005543 }
5544 else
5545 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005546 method_nr = crypt_get_method_nr(buf);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005547 key = buf->b_p_key;
Christian Brabandtaae58342023-04-23 17:50:22 +01005548 arg.cat_seed = mfp->mf_seed;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005549 }
Christian Brabandtaae58342023-04-23 17:50:22 +01005550
Bram Moolenaarbc563362015-06-09 18:35:25 +02005551 if (*key == NUL)
5552 return NULL;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005553
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005554 if (method_nr == CRYPT_M_ZIP)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005555 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005556 // For PKzip: Append the offset to the key, so that we use a different
5557 // key for every block.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005558 vim_snprintf((char *)salt, sizeof(salt), "%s%ld", key, (long)offset);
Christian Brabandtaae58342023-04-23 17:50:22 +01005559 arg.cat_seed = NULL;
5560 arg.cat_init_from_file = FALSE;
5561
5562 return crypt_create(method_nr, salt, &arg);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005563 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005564
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005565 // Using blowfish or better: add salt and seed. We use the byte offset
5566 // of the block for the salt.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005567 vim_snprintf((char *)salt, sizeof(salt), "%ld", (long)offset);
Christian Brabandtaae58342023-04-23 17:50:22 +01005568
5569 arg.cat_salt = salt;
5570 arg.cat_salt_len = (int)STRLEN(salt);
5571 arg.cat_seed_len = MF_SEED_LEN;
5572 arg.cat_add_len = 0;
5573 arg.cat_add = NULL;
5574 arg.cat_init_from_file = FALSE;
5575
5576 return crypt_create(method_nr, key, &arg);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005577}
5578
5579#endif
5580
5581
Bram Moolenaar071d4272004-06-13 20:20:40 +00005582#if defined(FEAT_BYTEOFF) || defined(PROTO)
5583
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005584#define MLCS_MAXL 800 // max no of lines in chunk
5585#define MLCS_MINL 400 // should be half of MLCS_MAXL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005586
5587/*
Bram Moolenaar0ad014c2010-07-25 14:00:46 +02005588 * Keep information for finding byte offset of a line, updtype may be one of:
Bram Moolenaar071d4272004-06-13 20:20:40 +00005589 * ML_CHNK_ADDLINE: Add len to parent chunk, possibly splitting it
5590 * Careful: ML_CHNK_ADDLINE may cause ml_find_line() to be called.
5591 * ML_CHNK_DELLINE: Subtract len from parent chunk, possibly deleting it
5592 * ML_CHNK_UPDLINE: Add len to parent chunk, as a signed entity.
5593 */
5594 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005595ml_updatechunk(
5596 buf_T *buf,
5597 linenr_T line,
5598 long len,
5599 int updtype)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005600{
5601 static buf_T *ml_upd_lastbuf = NULL;
5602 static linenr_T ml_upd_lastline;
5603 static linenr_T ml_upd_lastcurline;
5604 static int ml_upd_lastcurix;
5605
5606 linenr_T curline = ml_upd_lastcurline;
5607 int curix = ml_upd_lastcurix;
5608 long size;
5609 chunksize_T *curchnk;
5610 int rest;
5611 bhdr_T *hp;
5612 DATA_BL *dp;
5613
5614 if (buf->b_ml.ml_usedchunks == -1 || len == 0)
5615 return;
5616 if (buf->b_ml.ml_chunksize == NULL)
5617 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005618 buf->b_ml.ml_chunksize = ALLOC_MULT(chunksize_T, 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005619 if (buf->b_ml.ml_chunksize == NULL)
5620 {
5621 buf->b_ml.ml_usedchunks = -1;
5622 return;
5623 }
5624 buf->b_ml.ml_numchunks = 100;
5625 buf->b_ml.ml_usedchunks = 1;
5626 buf->b_ml.ml_chunksize[0].mlcs_numlines = 1;
5627 buf->b_ml.ml_chunksize[0].mlcs_totalsize = 1;
5628 }
5629
5630 if (updtype == ML_CHNK_UPDLINE && buf->b_ml.ml_line_count == 1)
5631 {
5632 /*
5633 * First line in empty buffer from ml_flush_line() -- reset
5634 */
5635 buf->b_ml.ml_usedchunks = 1;
5636 buf->b_ml.ml_chunksize[0].mlcs_numlines = 1;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01005637 buf->b_ml.ml_chunksize[0].mlcs_totalsize = (long)buf->b_ml.ml_line_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005638 return;
5639 }
5640
5641 /*
5642 * Find chunk that our line belongs to, curline will be at start of the
5643 * chunk.
5644 */
5645 if (buf != ml_upd_lastbuf || line != ml_upd_lastline + 1
5646 || updtype != ML_CHNK_ADDLINE)
5647 {
5648 for (curline = 1, curix = 0;
5649 curix < buf->b_ml.ml_usedchunks - 1
5650 && line >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines;
5651 curix++)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005652 curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005653 }
Bram Moolenaara9a8e042018-10-30 22:15:55 +01005654 else if (curix < buf->b_ml.ml_usedchunks - 1
5655 && line >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005656 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005657 // Adjust cached curix & curline
Bram Moolenaar071d4272004-06-13 20:20:40 +00005658 curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
5659 curix++;
5660 }
5661 curchnk = buf->b_ml.ml_chunksize + curix;
5662
5663 if (updtype == ML_CHNK_DELLINE)
Bram Moolenaar5a6404c2006-11-01 17:12:57 +00005664 len = -len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005665 curchnk->mlcs_totalsize += len;
5666 if (updtype == ML_CHNK_ADDLINE)
5667 {
5668 curchnk->mlcs_numlines++;
5669
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005670 // May resize here so we don't have to do it in both cases below
Bram Moolenaar071d4272004-06-13 20:20:40 +00005671 if (buf->b_ml.ml_usedchunks + 1 >= buf->b_ml.ml_numchunks)
5672 {
Bram Moolenaar9abd5c62015-02-10 18:34:01 +01005673 chunksize_T *t_chunksize = buf->b_ml.ml_chunksize;
5674
Bram Moolenaar071d4272004-06-13 20:20:40 +00005675 buf->b_ml.ml_numchunks = buf->b_ml.ml_numchunks * 3 / 2;
Bram Moolenaar51b6eb42020-08-22 15:19:18 +02005676 buf->b_ml.ml_chunksize = vim_realloc(buf->b_ml.ml_chunksize,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005677 sizeof(chunksize_T) * buf->b_ml.ml_numchunks);
5678 if (buf->b_ml.ml_chunksize == NULL)
5679 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005680 // Hmmmm, Give up on offset for this buffer
Bram Moolenaar9abd5c62015-02-10 18:34:01 +01005681 vim_free(t_chunksize);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005682 buf->b_ml.ml_usedchunks = -1;
5683 return;
5684 }
5685 }
5686
5687 if (buf->b_ml.ml_chunksize[curix].mlcs_numlines >= MLCS_MAXL)
5688 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005689 int count; // number of entries in block
Bram Moolenaar071d4272004-06-13 20:20:40 +00005690 int idx;
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005691 int end_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005692 int text_end;
5693 int linecnt;
5694
5695 mch_memmove(buf->b_ml.ml_chunksize + curix + 1,
5696 buf->b_ml.ml_chunksize + curix,
5697 (buf->b_ml.ml_usedchunks - curix) *
5698 sizeof(chunksize_T));
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005699 // Compute length of first half of lines in the split chunk
Bram Moolenaar071d4272004-06-13 20:20:40 +00005700 size = 0;
5701 linecnt = 0;
5702 while (curline < buf->b_ml.ml_line_count
5703 && linecnt < MLCS_MINL)
5704 {
5705 if ((hp = ml_find_line(buf, curline, ML_FIND)) == NULL)
5706 {
5707 buf->b_ml.ml_usedchunks = -1;
5708 return;
5709 }
5710 dp = (DATA_BL *)(hp->bh_data);
5711 count = (long)(buf->b_ml.ml_locked_high) -
5712 (long)(buf->b_ml.ml_locked_low) + 1;
5713 idx = curline - buf->b_ml.ml_locked_low;
5714 curline = buf->b_ml.ml_locked_high + 1;
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005715
5716 // compute index of last line to use in this MEMLINE
Bram Moolenaar071d4272004-06-13 20:20:40 +00005717 rest = count - idx;
5718 if (linecnt + rest > MLCS_MINL)
5719 {
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005720 end_idx = idx + MLCS_MINL - linecnt - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005721 linecnt = MLCS_MINL;
5722 }
5723 else
5724 {
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005725 end_idx = count - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005726 linecnt += rest;
5727 }
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01005728#ifdef FEAT_PROP_POPUP
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005729 if (buf->b_has_textprop)
5730 {
5731 int i;
5732
5733 // We cannot use the text pointers to get the text length,
5734 // the text prop info would also be counted. Go over the
5735 // lines.
5736 for (i = end_idx; i < idx; ++i)
Bram Moolenaare5a0e8c2022-08-09 21:37:55 +01005737 size += (int)STRLEN((char_u *)dp
5738 + (dp->db_index[i] & DB_INDEX_MASK)) + 1;
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005739 }
5740 else
5741#endif
5742 {
Bram Moolenaar14c75302021-08-15 14:28:40 +02005743 if (idx == 0) // first line in block, text at the end
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005744 text_end = dp->db_txt_end;
5745 else
5746 text_end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK);
Bram Moolenaare5a0e8c2022-08-09 21:37:55 +01005747 size += text_end
5748 - ((dp->db_index[end_idx]) & DB_INDEX_MASK);
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005749 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005750 }
5751 buf->b_ml.ml_chunksize[curix].mlcs_numlines = linecnt;
5752 buf->b_ml.ml_chunksize[curix + 1].mlcs_numlines -= linecnt;
5753 buf->b_ml.ml_chunksize[curix].mlcs_totalsize = size;
5754 buf->b_ml.ml_chunksize[curix + 1].mlcs_totalsize -= size;
5755 buf->b_ml.ml_usedchunks++;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005756 ml_upd_lastbuf = NULL; // Force recalc of curix & curline
Bram Moolenaar071d4272004-06-13 20:20:40 +00005757 return;
5758 }
5759 else if (buf->b_ml.ml_chunksize[curix].mlcs_numlines >= MLCS_MINL
5760 && curix == buf->b_ml.ml_usedchunks - 1
5761 && buf->b_ml.ml_line_count - line <= 1)
5762 {
5763 /*
Bram Moolenaar8e7d6222020-12-18 19:49:56 +01005764 * We are in the last chunk and it is cheap to create a new one
Bram Moolenaar071d4272004-06-13 20:20:40 +00005765 * after this. Do it now to avoid the loop above later on
5766 */
5767 curchnk = buf->b_ml.ml_chunksize + curix + 1;
5768 buf->b_ml.ml_usedchunks++;
5769 if (line == buf->b_ml.ml_line_count)
5770 {
5771 curchnk->mlcs_numlines = 0;
5772 curchnk->mlcs_totalsize = 0;
5773 }
5774 else
5775 {
5776 /*
5777 * Line is just prior to last, move count for last
5778 * This is the common case when loading a new file
5779 */
5780 hp = ml_find_line(buf, buf->b_ml.ml_line_count, ML_FIND);
5781 if (hp == NULL)
5782 {
5783 buf->b_ml.ml_usedchunks = -1;
5784 return;
5785 }
5786 dp = (DATA_BL *)(hp->bh_data);
5787 if (dp->db_line_count == 1)
5788 rest = dp->db_txt_end - dp->db_txt_start;
5789 else
5790 rest =
5791 ((dp->db_index[dp->db_line_count - 2]) & DB_INDEX_MASK)
5792 - dp->db_txt_start;
5793 curchnk->mlcs_totalsize = rest;
5794 curchnk->mlcs_numlines = 1;
5795 curchnk[-1].mlcs_totalsize -= rest;
5796 curchnk[-1].mlcs_numlines -= 1;
5797 }
5798 }
5799 }
5800 else if (updtype == ML_CHNK_DELLINE)
5801 {
5802 curchnk->mlcs_numlines--;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005803 ml_upd_lastbuf = NULL; // Force recalc of curix & curline
Bram Moolenaare5a0e8c2022-08-09 21:37:55 +01005804 if (curix < buf->b_ml.ml_usedchunks - 1
5805 && curchnk->mlcs_numlines + curchnk[1].mlcs_numlines
5806 <= MLCS_MINL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005807 {
5808 curix++;
5809 curchnk = buf->b_ml.ml_chunksize + curix;
5810 }
5811 else if (curix == 0 && curchnk->mlcs_numlines <= 0)
5812 {
5813 buf->b_ml.ml_usedchunks--;
5814 mch_memmove(buf->b_ml.ml_chunksize, buf->b_ml.ml_chunksize + 1,
5815 buf->b_ml.ml_usedchunks * sizeof(chunksize_T));
5816 return;
5817 }
5818 else if (curix == 0 || (curchnk->mlcs_numlines > 10
Bram Moolenaare5a0e8c2022-08-09 21:37:55 +01005819 && curchnk->mlcs_numlines + curchnk[-1].mlcs_numlines
5820 > MLCS_MINL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005821 {
5822 return;
5823 }
5824
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005825 // Collapse chunks
Bram Moolenaar071d4272004-06-13 20:20:40 +00005826 curchnk[-1].mlcs_numlines += curchnk->mlcs_numlines;
5827 curchnk[-1].mlcs_totalsize += curchnk->mlcs_totalsize;
5828 buf->b_ml.ml_usedchunks--;
5829 if (curix < buf->b_ml.ml_usedchunks)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005830 mch_memmove(buf->b_ml.ml_chunksize + curix,
5831 buf->b_ml.ml_chunksize + curix + 1,
5832 (buf->b_ml.ml_usedchunks - curix) *
5833 sizeof(chunksize_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005834 return;
5835 }
5836 ml_upd_lastbuf = buf;
5837 ml_upd_lastline = line;
5838 ml_upd_lastcurline = curline;
5839 ml_upd_lastcurix = curix;
5840}
5841
5842/*
5843 * Find offset for line or line with offset.
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005844 * Find line with offset if "lnum" is 0; return remaining offset in offp
5845 * Find offset of line if "lnum" > 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00005846 * return -1 if information is not available
5847 */
5848 long
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005849ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005850{
5851 linenr_T curline;
5852 int curix;
5853 long size;
5854 bhdr_T *hp;
5855 DATA_BL *dp;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005856 int count; // number of entries in block
Bram Moolenaar071d4272004-06-13 20:20:40 +00005857 int idx;
5858 int start_idx;
5859 int text_end;
5860 long offset;
5861 int len;
5862 int ffdos = (get_fileformat(buf) == EOL_DOS);
5863 int extra = 0;
5864
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005865 // take care of cached line first
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005866 ml_flush_line(curbuf);
5867
Bram Moolenaar071d4272004-06-13 20:20:40 +00005868 if (buf->b_ml.ml_usedchunks == -1
5869 || buf->b_ml.ml_chunksize == NULL
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005870 || lnum < 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005871 return -1;
5872
5873 if (offp == NULL)
5874 offset = 0;
5875 else
5876 offset = *offp;
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005877 if (lnum == 0 && offset <= 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005878 return 1; // Not a "find offset" and offset 0 _must_ be in line 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00005879 /*
5880 * Find the last chunk before the one containing our line. Last chunk is
Bram Moolenaar14c75302021-08-15 14:28:40 +02005881 * special because it will never qualify.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005882 */
5883 curline = 1;
5884 curix = size = 0;
5885 while (curix < buf->b_ml.ml_usedchunks - 1
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005886 && ((lnum != 0
5887 && lnum >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005888 || (offset != 0
5889 && offset > size + buf->b_ml.ml_chunksize[curix].mlcs_totalsize
=?UTF-8?q?Dundar=20G=C3=B6c?=d5cec1f2022-01-29 15:19:23 +00005890 + (long)ffdos * buf->b_ml.ml_chunksize[curix].mlcs_numlines)))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005891 {
5892 curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
5893 size += buf->b_ml.ml_chunksize[curix].mlcs_totalsize;
5894 if (offset && ffdos)
5895 size += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
5896 curix++;
5897 }
5898
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005899 while ((lnum != 0 && curline < lnum) || (offset != 0 && size < offset))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005900 {
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005901#ifdef FEAT_PROP_POPUP
5902 size_t textprop_total = 0;
5903#endif
5904
Bram Moolenaar071d4272004-06-13 20:20:40 +00005905 if (curline > buf->b_ml.ml_line_count
5906 || (hp = ml_find_line(buf, curline, ML_FIND)) == NULL)
5907 return -1;
5908 dp = (DATA_BL *)(hp->bh_data);
5909 count = (long)(buf->b_ml.ml_locked_high) -
5910 (long)(buf->b_ml.ml_locked_low) + 1;
5911 start_idx = idx = curline - buf->b_ml.ml_locked_low;
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005912 if (idx == 0) // first line in block, text at the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00005913 text_end = dp->db_txt_end;
5914 else
5915 text_end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005916 // Compute index of last line to use in this MEMLINE
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005917 if (lnum != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005918 {
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005919 if (curline + (count - idx) >= lnum)
5920 idx += lnum - curline - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005921 else
5922 idx = count - 1;
5923 }
5924 else
5925 {
5926 extra = 0;
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005927 for (;;)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005928 {
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005929#ifdef FEAT_PROP_POPUP
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005930 size_t textprop_size = 0;
5931
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005932 if (buf->b_has_textprop)
5933 {
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005934 char_u *l1, *l2;
5935
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005936 // compensate for the extra bytes taken by textprops
5937 l1 = (char_u *)dp + ((dp->db_index[idx]) & DB_INDEX_MASK);
5938 l2 = (char_u *)dp + (idx == 0 ? dp->db_txt_end
5939 : ((dp->db_index[idx - 1]) & DB_INDEX_MASK));
5940 textprop_size = (l2 - l1) - (STRLEN(l1) + 1);
5941 }
5942#endif
5943 if (!(offset >= size
5944 + text_end - (int)((dp->db_index[idx]) & DB_INDEX_MASK)
5945#ifdef FEAT_PROP_POPUP
Bram Moolenaar94b6fb72020-01-17 21:00:59 +01005946 - (long)(textprop_total + textprop_size)
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005947#endif
5948 + ffdos))
5949 break;
5950
Bram Moolenaar071d4272004-06-13 20:20:40 +00005951 if (ffdos)
5952 size++;
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005953#ifdef FEAT_PROP_POPUP
5954 textprop_total += textprop_size;
5955#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005956 if (idx == count - 1)
5957 {
5958 extra = 1;
5959 break;
5960 }
5961 idx++;
5962 }
5963 }
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01005964#ifdef FEAT_PROP_POPUP
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005965 if (buf->b_has_textprop && lnum != 0)
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005966 {
5967 int i;
5968
5969 // cannot use the db_index pointer, need to get the actual text
5970 // lengths.
5971 len = 0;
5972 for (i = start_idx; i <= idx; ++i)
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005973 {
5974 char_u *p = (char_u *)dp + ((dp->db_index[i]) & DB_INDEX_MASK);
5975 len += (int)STRLEN(p) + 1;
5976 }
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005977 }
5978 else
5979#endif
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005980 len = text_end - ((dp->db_index[idx]) & DB_INDEX_MASK)
5981#ifdef FEAT_PROP_POPUP
5982 - (long)textprop_total
5983#endif
5984 ;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005985 size += len;
5986 if (offset != 0 && size >= offset)
5987 {
5988 if (size + ffdos == offset)
5989 *offp = 0;
5990 else if (idx == start_idx)
5991 *offp = offset - size + len;
5992 else
5993 *offp = offset - size + len
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005994 - (text_end - ((dp->db_index[idx - 1]) & DB_INDEX_MASK))
5995#ifdef FEAT_PROP_POPUP
5996 + (long)textprop_total
5997#endif
5998 ;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005999 curline += idx - start_idx + extra;
6000 if (curline > buf->b_ml.ml_line_count)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01006001 return -1; // exactly one byte beyond the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00006002 return curline;
6003 }
6004 curline = buf->b_ml.ml_locked_high + 1;
6005 }
6006
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00006007 if (lnum != 0)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006008 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01006009 // Count extra CR characters.
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006010 if (ffdos)
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00006011 size += lnum - 1;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006012
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01006013 // Don't count the last line break if 'noeol' and ('bin' or
6014 // 'nofixeol').
Bram Moolenaar34d72d42015-07-17 14:18:08 +02006015 if ((!buf->b_p_fixeol || buf->b_p_bin) && !buf->b_p_eol
Bram Moolenaarc26f7c62018-08-20 22:53:04 +02006016 && lnum > buf->b_ml.ml_line_count)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00006017 size -= ffdos + 1;
6018 }
6019
Bram Moolenaar071d4272004-06-13 20:20:40 +00006020 return size;
6021}
6022
6023/*
6024 * Goto byte in buffer with offset 'cnt'.
6025 */
6026 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01006027goto_byte(long cnt)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006028{
6029 long boff = cnt;
6030 linenr_T lnum;
6031
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01006032 ml_flush_line(curbuf); // cached line may be dirty
Bram Moolenaar071d4272004-06-13 20:20:40 +00006033 setpcmark();
6034 if (boff)
6035 --boff;
6036 lnum = ml_find_line_or_offset(curbuf, (linenr_T)0, &boff);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01006037 if (lnum < 1) // past the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00006038 {
6039 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
6040 curwin->w_curswant = MAXCOL;
6041 coladvance((colnr_T)MAXCOL);
6042 }
6043 else
6044 {
6045 curwin->w_cursor.lnum = lnum;
6046 curwin->w_cursor.col = (colnr_T)boff;
Bram Moolenaar943d2b52005-12-02 00:50:49 +00006047 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006048 curwin->w_set_curswant = TRUE;
6049 }
6050 check_cursor();
6051
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01006052 // Make sure the cursor is on the first byte of a multi-byte char.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006053 if (has_mbyte)
6054 mb_adjust_cursor();
Bram Moolenaar071d4272004-06-13 20:20:40 +00006055}
6056#endif