blob: 7cbe559e65bb62112d2737045e83a4c43325355c [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
Christian Brabandtf573c6e2021-06-20 14:02:16 +020067#define BLOCK0_ID1_C3 'S' // block 0 id 1 'cm' 3 - but not actually used
Bram Moolenaar8f4ac012014-08-10 13:38:34 +020068
69#if defined(FEAT_CRYPT)
70static int id1_codes[] = {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010071 BLOCK0_ID1_C0, // CRYPT_M_ZIP
72 BLOCK0_ID1_C1, // CRYPT_M_BF
73 BLOCK0_ID1_C2, // CRYPT_M_BF2
Christian Brabandtf573c6e2021-06-20 14:02:16 +020074 BLOCK0_ID1_C3, // CRYPT_M_SOD - Unused!
Bram Moolenaar8f4ac012014-08-10 13:38:34 +020075};
76#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000077
78/*
79 * pointer to a block, used in a pointer block
80 */
81struct pointer_entry
82{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010083 blocknr_T pe_bnum; // block number
84 linenr_T pe_line_count; // number of lines in this branch
85 linenr_T pe_old_lnum; // lnum for this block (for recovery)
86 int pe_page_count; // number of pages in block pe_bnum
Bram Moolenaar071d4272004-06-13 20:20:40 +000087};
88
89/*
90 * A pointer block contains a list of branches in the tree.
91 */
92struct pointer_block
93{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010094 short_u pb_id; // ID for pointer block: PTR_ID
95 short_u pb_count; // number of pointers in this block
96 short_u pb_count_max; // maximum value for pb_count
97 PTR_EN pb_pointer[1]; // list of pointers to blocks (actually longer)
98 // followed by empty space until end of page
Bram Moolenaar071d4272004-06-13 20:20:40 +000099};
100
101/*
102 * A data block is a leaf in the tree.
103 *
104 * The text of the lines is at the end of the block. The text of the first line
105 * in the block is put at the end, the text of the second line in front of it,
106 * etc. Thus the order of the lines is the opposite of the line number.
107 */
108struct data_block
109{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100110 short_u db_id; // ID for data block: DATA_ID
111 unsigned db_free; // free space available
112 unsigned db_txt_start; // byte where text starts
113 unsigned db_txt_end; // byte just after data block
114 linenr_T db_line_count; // number of lines in this block
115 unsigned db_index[1]; // index for start of line (actually bigger)
Bram Moolenaar4b96df52020-01-26 22:00:26 +0100116 // followed by empty space up to db_txt_start
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100117 // followed by the text in the lines until
118 // end of page
Bram Moolenaar071d4272004-06-13 20:20:40 +0000119};
120
121/*
122 * The low bits of db_index hold the actual index. The topmost bit is
123 * used for the global command to be able to mark a line.
124 * This method is not clean, but otherwise there would be at least one extra
125 * byte used for each line.
126 * The mark has to be in this place to keep it with the correct line when other
127 * lines are inserted or deleted.
128 */
129#define DB_MARKED ((unsigned)1 << ((sizeof(unsigned) * 8) - 1))
130#define DB_INDEX_MASK (~DB_MARKED)
131
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100132#define INDEX_SIZE (sizeof(unsigned)) // size of one db_index entry
133#define HEADER_SIZE (sizeof(DATA_BL) - INDEX_SIZE) // size of data block header
Bram Moolenaar071d4272004-06-13 20:20:40 +0000134
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100135#define B0_FNAME_SIZE_ORG 900 // what it was in older versions
136#define B0_FNAME_SIZE_NOCRYPT 898 // 2 bytes used for other things
137#define B0_FNAME_SIZE_CRYPT 890 // 10 bytes used for other things
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000138#define B0_UNAME_SIZE 40
139#define B0_HNAME_SIZE 40
Bram Moolenaar071d4272004-06-13 20:20:40 +0000140/*
141 * Restrict the numbers to 32 bits, otherwise most compilers will complain.
142 * This won't detect a 64 bit machine that only swaps a byte in the top 32
143 * bits, but that is crazy anyway.
144 */
145#define B0_MAGIC_LONG 0x30313233L
146#define B0_MAGIC_INT 0x20212223L
147#define B0_MAGIC_SHORT 0x10111213L
148#define B0_MAGIC_CHAR 0x55
149
150/*
151 * Block zero holds all info about the swap file.
152 *
153 * NOTE: DEFINITION OF BLOCK 0 SHOULD NOT CHANGE! It would make all existing
154 * swap files unusable!
155 *
156 * If size of block0 changes anyway, adjust MIN_SWAP_PAGE_SIZE in vim.h!!
157 *
Bram Moolenaarbae0c162007-05-10 19:30:25 +0000158 * This block is built up of single bytes, to make it portable across
Bram Moolenaar071d4272004-06-13 20:20:40 +0000159 * different machines. b0_magic_* is used to check the byte order and size of
160 * variables, because the rest of the swap file is not portable.
161 */
162struct block0
163{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100164 char_u b0_id[2]; // id for block 0: BLOCK0_ID0 and BLOCK0_ID1,
165 // BLOCK0_ID1_C0, BLOCK0_ID1_C1, etc.
166 char_u b0_version[10]; // Vim version string
167 char_u b0_page_size[4];// number of bytes per page
168 char_u b0_mtime[4]; // last modification time of file
169 char_u b0_ino[4]; // inode of b0_fname
170 char_u b0_pid[4]; // process id of creator (or 0)
171 char_u b0_uname[B0_UNAME_SIZE]; // name of user (uid if no name)
172 char_u b0_hname[B0_HNAME_SIZE]; // host name (if it has a name)
173 char_u b0_fname[B0_FNAME_SIZE_ORG]; // name of file being edited
174 long b0_magic_long; // check for byte order of long
175 int b0_magic_int; // check for byte order of int
176 short b0_magic_short; // check for byte order of short
177 char_u b0_magic_char; // check for last char
Bram Moolenaar071d4272004-06-13 20:20:40 +0000178};
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000179
180/*
Bram Moolenaar4770d092006-01-12 23:22:24 +0000181 * Note: b0_dirty and b0_flags are put at the end of the file name. For very
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000182 * long file names in older versions of Vim they are invalid.
183 * The 'fileencoding' comes before b0_flags, with a NUL in front. But only
184 * when there is room, for very long file names it's omitted.
185 */
186#define B0_DIRTY 0x55
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200187#define b0_dirty b0_fname[B0_FNAME_SIZE_ORG - 1]
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000188
189/*
190 * The b0_flags field is new in Vim 7.0.
191 */
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200192#define b0_flags b0_fname[B0_FNAME_SIZE_ORG - 2]
193
194/*
195 * Crypt seed goes here, 8 bytes. New in Vim 7.3.
196 * Without encryption these bytes may be used for 'fenc'.
197 */
198#define b0_seed b0_fname[B0_FNAME_SIZE_ORG - 2 - MF_SEED_LEN]
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000199
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100200// The lowest two bits contain the fileformat. Zero means it's not set
201// (compatible with Vim 6.x), otherwise it's EOL_UNIX + 1, EOL_DOS + 1 or
202// EOL_MAC + 1.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000203#define B0_FF_MASK 3
204
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100205// Swap file is in directory of edited file. Used to find the file from
206// different mount points.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000207#define B0_SAME_DIR 4
208
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100209// The 'fileencoding' is at the end of b0_fname[], with a NUL in front of it.
210// When empty there is only the NUL.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000211#define B0_HAS_FENC 8
Bram Moolenaar071d4272004-06-13 20:20:40 +0000212
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100213#define STACK_INCR 5 // nr of entries added to ml_stack at a time
Bram Moolenaar071d4272004-06-13 20:20:40 +0000214
215/*
216 * The line number where the first mark may be is remembered.
217 * If it is 0 there are no marks at all.
218 * (always used for the current buffer only, no buffer change possible while
219 * executing a global command).
220 */
221static linenr_T lowest_marked = 0;
222
223/*
224 * arguments for ml_find_line()
225 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100226#define ML_DELETE 0x11 // delete line
227#define ML_INSERT 0x12 // insert line
228#define ML_FIND 0x13 // just find the line
229#define ML_FLUSH 0x02 // flush locked block
kylo252ae6f1d82022-02-16 19:24:07 +0000230#define ML_SIMPLE(x) ((x) & 0x10) // DEL, INS or FIND
Bram Moolenaar071d4272004-06-13 20:20:40 +0000231
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100232// argument for ml_upd_block0()
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200233typedef enum {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100234 UB_FNAME = 0 // update timestamp and filename
235 , UB_SAME_DIR // update the B0_SAME_DIR flag
236 , UB_CRYPT // update crypt key
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200237} upd_block0_T;
238
239#ifdef FEAT_CRYPT
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100240static void ml_set_b0_crypt(buf_T *buf, ZERO_BL *b0p);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200241#endif
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100242static void ml_upd_block0(buf_T *buf, upd_block0_T what);
243static void set_b0_fname(ZERO_BL *, buf_T *buf);
244static void set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100245static void add_b0_fenc(ZERO_BL *b0p, buf_T *buf);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100246static time_t swapfile_info(char_u *);
247static int recov_file_names(char_u **, char_u *, int prepend_dot);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100248static char_u *findswapname(buf_T *, char_u **, char_u *);
249static void ml_flush_line(buf_T *);
250static bhdr_T *ml_new_data(memfile_T *, int, int);
251static bhdr_T *ml_new_ptr(memfile_T *);
252static bhdr_T *ml_find_line(buf_T *, linenr_T, int);
253static int ml_add_stack(buf_T *);
254static void ml_lineadd(buf_T *, int);
255static int b0_magic_wrong(ZERO_BL *);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000256#ifdef CHECK_INODE
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100257static int fnamecmp_ino(char_u *, char_u *, long);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000258#endif
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100259static void long_to_char(long, char_u *);
260static long char_to_long(char_u *);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200261#ifdef FEAT_CRYPT
Bram Moolenaar8767f522016-07-01 17:17:39 +0200262static cryptstate_T *ml_crypt_prepare(memfile_T *mfp, off_T offset, int reading);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200263#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000264#ifdef FEAT_BYTEOFF
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100265static void ml_updatechunk(buf_T *buf, long line, long len, int updtype);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000266#endif
267
268/*
Bram Moolenaar4770d092006-01-12 23:22:24 +0000269 * Open a new memline for "buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +0000270 *
Bram Moolenaar4770d092006-01-12 23:22:24 +0000271 * Return FAIL for failure, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000272 */
273 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100274ml_open(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000275{
276 memfile_T *mfp;
277 bhdr_T *hp = NULL;
278 ZERO_BL *b0p;
279 PTR_BL *pp;
280 DATA_BL *dp;
281
Bram Moolenaar4770d092006-01-12 23:22:24 +0000282 /*
283 * init fields in memline struct
284 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100285 buf->b_ml.ml_stack_size = 0; // no stack yet
286 buf->b_ml.ml_stack = NULL; // no stack yet
287 buf->b_ml.ml_stack_top = 0; // nothing in the stack
288 buf->b_ml.ml_locked = NULL; // no cached block
289 buf->b_ml.ml_line_lnum = 0; // no cached line
Bram Moolenaar071d4272004-06-13 20:20:40 +0000290#ifdef FEAT_BYTEOFF
Bram Moolenaar4770d092006-01-12 23:22:24 +0000291 buf->b_ml.ml_chunksize = NULL;
Bram Moolenaardf9070e2021-08-25 17:31:37 +0200292 buf->b_ml.ml_usedchunks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000293#endif
294
Bram Moolenaare1004402020-10-24 20:49:43 +0200295 if (cmdmod.cmod_flags & CMOD_NOSWAPFILE)
Bram Moolenaar5803ae62014-03-23 16:04:02 +0100296 buf->b_p_swf = FALSE;
297
Bram Moolenaar4770d092006-01-12 23:22:24 +0000298 /*
299 * When 'updatecount' is non-zero swap file may be opened later.
300 */
301 if (p_uc && buf->b_p_swf)
302 buf->b_may_swap = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000303 else
Bram Moolenaar4770d092006-01-12 23:22:24 +0000304 buf->b_may_swap = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000305
Bram Moolenaar4770d092006-01-12 23:22:24 +0000306 /*
307 * Open the memfile. No swap file is created yet.
308 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000309 mfp = mf_open(NULL, 0);
310 if (mfp == NULL)
311 goto error;
312
Bram Moolenaar4770d092006-01-12 23:22:24 +0000313 buf->b_ml.ml_mfp = mfp;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200314#ifdef FEAT_CRYPT
315 mfp->mf_buffer = buf;
316#endif
Bram Moolenaar4770d092006-01-12 23:22:24 +0000317 buf->b_ml.ml_flags = ML_EMPTY;
318 buf->b_ml.ml_line_count = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000319
Bram Moolenaar071d4272004-06-13 20:20:40 +0000320/*
321 * fill block0 struct and write page 0
322 */
323 if ((hp = mf_new(mfp, FALSE, 1)) == NULL)
324 goto error;
325 if (hp->bh_bnum != 0)
326 {
Bram Moolenaar9a846fb2022-01-01 21:59:18 +0000327 iemsg(_(e_didnt_get_block_nr_zero));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000328 goto error;
329 }
330 b0p = (ZERO_BL *)(hp->bh_data);
331
332 b0p->b0_id[0] = BLOCK0_ID0;
333 b0p->b0_id[1] = BLOCK0_ID1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000334 b0p->b0_magic_long = (long)B0_MAGIC_LONG;
335 b0p->b0_magic_int = (int)B0_MAGIC_INT;
336 b0p->b0_magic_short = (short)B0_MAGIC_SHORT;
337 b0p->b0_magic_char = B0_MAGIC_CHAR;
Bram Moolenaar22c10562018-05-26 17:35:27 +0200338 mch_memmove(b0p->b0_version, "VIM ", 4);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000339 STRNCPY(b0p->b0_version + 4, Version, 6);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000340 long_to_char((long)mfp->mf_page_size, b0p->b0_page_size);
Bram Moolenaar4770d092006-01-12 23:22:24 +0000341
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000342#ifdef FEAT_SPELL
343 if (!buf->b_spell)
344#endif
Bram Moolenaar4770d092006-01-12 23:22:24 +0000345 {
346 b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0;
347 b0p->b0_flags = get_fileformat(buf) + 1;
348 set_b0_fname(b0p, buf);
349 (void)get_user_name(b0p->b0_uname, B0_UNAME_SIZE);
350 b0p->b0_uname[B0_UNAME_SIZE - 1] = NUL;
351 mch_get_host_name(b0p->b0_hname, B0_HNAME_SIZE);
352 b0p->b0_hname[B0_HNAME_SIZE - 1] = NUL;
353 long_to_char(mch_get_pid(), b0p->b0_pid);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200354#ifdef FEAT_CRYPT
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200355 ml_set_b0_crypt(buf, b0p);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200356#endif
Bram Moolenaar4770d092006-01-12 23:22:24 +0000357 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000358
359 /*
360 * Always sync block number 0 to disk, so we can check the file name in
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200361 * the swap file in findswapname(). Don't do this for a help files or
362 * a spell buffer though.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000363 * Only works when there's a swapfile, otherwise it's done when the file
364 * is created.
365 */
366 mf_put(mfp, hp, TRUE, FALSE);
Bram Moolenaar4770d092006-01-12 23:22:24 +0000367 if (!buf->b_help && !B_SPELL(buf))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000368 (void)mf_sync(mfp, 0);
369
Bram Moolenaar4770d092006-01-12 23:22:24 +0000370 /*
371 * Fill in root pointer block and write page 1.
372 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000373 if ((hp = ml_new_ptr(mfp)) == NULL)
374 goto error;
375 if (hp->bh_bnum != 1)
376 {
Bram Moolenaar9a846fb2022-01-01 21:59:18 +0000377 iemsg(_(e_didnt_get_block_nr_one));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000378 goto error;
379 }
380 pp = (PTR_BL *)(hp->bh_data);
381 pp->pb_count = 1;
382 pp->pb_pointer[0].pe_bnum = 2;
383 pp->pb_pointer[0].pe_page_count = 1;
384 pp->pb_pointer[0].pe_old_lnum = 1;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100385 pp->pb_pointer[0].pe_line_count = 1; // line count after insertion
Bram Moolenaar071d4272004-06-13 20:20:40 +0000386 mf_put(mfp, hp, TRUE, FALSE);
387
Bram Moolenaar4770d092006-01-12 23:22:24 +0000388 /*
389 * Allocate first data block and create an empty line 1.
390 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000391 if ((hp = ml_new_data(mfp, FALSE, 1)) == NULL)
392 goto error;
393 if (hp->bh_bnum != 2)
394 {
Bram Moolenaar9a846fb2022-01-01 21:59:18 +0000395 iemsg(_(e_didnt_get_block_nr_two));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000396 goto error;
397 }
398
399 dp = (DATA_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100400 dp->db_index[0] = --dp->db_txt_start; // at end of block
Bram Moolenaar071d4272004-06-13 20:20:40 +0000401 dp->db_free -= 1 + INDEX_SIZE;
402 dp->db_line_count = 1;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100403 *((char_u *)dp + dp->db_txt_start) = NUL; // empty line
Bram Moolenaar071d4272004-06-13 20:20:40 +0000404
405 return OK;
406
407error:
408 if (mfp != NULL)
409 {
410 if (hp)
411 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100412 mf_close(mfp, TRUE); // will also free(mfp->mf_fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000413 }
Bram Moolenaar4770d092006-01-12 23:22:24 +0000414 buf->b_ml.ml_mfp = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000415 return FAIL;
416}
417
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200418#if defined(FEAT_CRYPT) || defined(PROTO)
419/*
Bram Moolenaar2be79502014-08-13 21:58:28 +0200420 * Prepare encryption for "buf" for the current key and method.
421 */
422 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100423ml_set_mfp_crypt(buf_T *buf)
Bram Moolenaar2be79502014-08-13 21:58:28 +0200424{
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000425 if (*buf->b_p_key == NUL)
426 return;
Bram Moolenaar2be79502014-08-13 21:58:28 +0200427
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000428 int method_nr = crypt_get_method_nr(buf);
429
430 if (method_nr > CRYPT_M_ZIP && method_nr < CRYPT_M_SOD)
431 {
432 // Generate a seed and store it in the memfile.
433 sha2_seed(buf->b_ml.ml_mfp->mf_seed, MF_SEED_LEN, NULL, 0);
Bram Moolenaar2be79502014-08-13 21:58:28 +0200434 }
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000435#ifdef FEAT_SODIUM
436 else if (method_nr == CRYPT_M_SOD)
437 crypt_sodium_randombytes_buf(buf->b_ml.ml_mfp->mf_seed,
438 MF_SEED_LEN);
439#endif
Bram Moolenaar2be79502014-08-13 21:58:28 +0200440}
441
442/*
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200443 * Prepare encryption for "buf" with block 0 "b0p".
444 */
445 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100446ml_set_b0_crypt(buf_T *buf, ZERO_BL *b0p)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200447{
448 if (*buf->b_p_key == NUL)
449 b0p->b0_id[1] = BLOCK0_ID1;
450 else
451 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200452 int method_nr = crypt_get_method_nr(buf);
453
454 b0p->b0_id[1] = id1_codes[method_nr];
Christian Brabandtf573c6e2021-06-20 14:02:16 +0200455 if (method_nr > CRYPT_M_ZIP && method_nr < CRYPT_M_SOD)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200456 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100457 // Generate a seed and store it in block 0 and in the memfile.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200458 sha2_seed(&b0p->b0_seed, MF_SEED_LEN, NULL, 0);
459 mch_memmove(buf->b_ml.ml_mfp->mf_seed, &b0p->b0_seed, MF_SEED_LEN);
460 }
461 }
462}
463
464/*
465 * Called after the crypt key or 'cryptmethod' was changed for "buf".
466 * Will apply this to the swapfile.
467 * "old_key" is the previous key. It is equal to buf->b_p_key when
468 * 'cryptmethod' is changed.
Bram Moolenaar49771f42010-07-20 17:32:38 +0200469 * "old_cm" is the previous 'cryptmethod'. It is equal to the current
470 * 'cryptmethod' when 'key' is changed.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200471 */
472 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100473ml_set_crypt_key(
474 buf_T *buf,
475 char_u *old_key,
476 char_u *old_cm)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200477{
478 memfile_T *mfp = buf->b_ml.ml_mfp;
479 bhdr_T *hp;
480 int page_count;
481 int idx;
482 long error;
483 infoptr_T *ip;
484 PTR_BL *pp;
485 DATA_BL *dp;
486 blocknr_T bnum;
487 int top;
Bram Moolenaarbc563362015-06-09 18:35:25 +0200488 int old_method;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200489
Christian Brabandtf573c6e2021-06-20 14:02:16 +0200490 if (mfp == NULL || mfp->mf_fd < 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100491 return; // no memfile yet, nothing to do
Bram Moolenaarbc563362015-06-09 18:35:25 +0200492 old_method = crypt_method_nr_from_name(old_cm);
493
Christian Brabandt226b28b2021-06-21 21:08:08 +0200494 // Swapfile encryption not supported by XChaCha20
495 if (crypt_get_method_nr(buf) == CRYPT_M_SOD && *buf->b_p_key != NUL)
Christian Brabandtf573c6e2021-06-20 14:02:16 +0200496 {
497 // close the swapfile
498 mf_close_file(buf, TRUE);
499 buf->b_p_swf = FALSE;
500 return;
501 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100502 // First make sure the swapfile is in a consistent state, using the old
503 // key and method.
Bram Moolenaarbc563362015-06-09 18:35:25 +0200504 {
505 char_u *new_key = buf->b_p_key;
506 char_u *new_buf_cm = buf->b_p_cm;
507
508 buf->b_p_key = old_key;
509 buf->b_p_cm = old_cm;
510 ml_preserve(buf, FALSE);
511 buf->b_p_key = new_key;
512 buf->b_p_cm = new_buf_cm;
513 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200514
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100515 // Set the key, method and seed to be used for reading, these must be the
516 // old values.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200517 mfp->mf_old_key = old_key;
Bram Moolenaarbc563362015-06-09 18:35:25 +0200518 mfp->mf_old_cm = old_method;
519 if (old_method > 0 && *old_key != NUL)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200520 mch_memmove(mfp->mf_old_seed, mfp->mf_seed, MF_SEED_LEN);
521
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100522 // Update block 0 with the crypt flag and may set a new seed.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200523 ml_upd_block0(buf, UB_CRYPT);
524
525 if (mfp->mf_infile_count > 2)
526 {
527 /*
528 * Need to read back all data blocks from disk, decrypt them with the
529 * old key/method and mark them to be written. The algorithm is
530 * similar to what happens in ml_recover(), but we skip negative block
531 * numbers.
532 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100533 ml_flush_line(buf); // flush buffered line
534 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush locked block
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200535
536 hp = NULL;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100537 bnum = 1; // start with block 1
538 page_count = 1; // which is 1 page
539 idx = 0; // start with first index in block 1
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200540 error = 0;
541 buf->b_ml.ml_stack_top = 0;
Bram Moolenaard23a8232018-02-10 18:45:26 +0100542 VIM_CLEAR(buf->b_ml.ml_stack);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100543 buf->b_ml.ml_stack_size = 0; // no stack yet
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200544
545 for ( ; !got_int; line_breakcheck())
546 {
547 if (hp != NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100548 mf_put(mfp, hp, FALSE, FALSE); // release previous block
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200549
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100550 // get the block (pointer or data)
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +0000551 if ((hp = mf_get(mfp, bnum, page_count)) == NULL)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200552 {
553 if (bnum == 1)
554 break;
555 ++error;
556 }
557 else
558 {
559 pp = (PTR_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100560 if (pp->pb_id == PTR_ID) // it is a pointer block
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200561 {
562 if (pp->pb_count == 0)
563 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100564 // empty block?
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200565 ++error;
566 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100567 else if (idx < (int)pp->pb_count) // go a block deeper
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200568 {
569 if (pp->pb_pointer[idx].pe_bnum < 0)
570 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100571 // Skip data block with negative block number.
572 // Should not happen, because of the ml_preserve()
573 // above. Get same block again for next index.
Bram Moolenaar4b7214e2019-01-03 21:55:32 +0100574 ++idx;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200575 continue;
576 }
577
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100578 // going one block deeper in the tree, new entry in
579 // stack
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200580 if ((top = ml_add_stack(buf)) < 0)
581 {
582 ++error;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100583 break; // out of memory
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200584 }
585 ip = &(buf->b_ml.ml_stack[top]);
586 ip->ip_bnum = bnum;
587 ip->ip_index = idx;
588
589 bnum = pp->pb_pointer[idx].pe_bnum;
590 page_count = pp->pb_pointer[idx].pe_page_count;
Bram Moolenaarbc563362015-06-09 18:35:25 +0200591 idx = 0;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200592 continue;
593 }
594 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100595 else // not a pointer block
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200596 {
597 dp = (DATA_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100598 if (dp->db_id != DATA_ID) // block id wrong
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200599 ++error;
600 else
601 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100602 // It is a data block, need to write it back to disk.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200603 mf_put(mfp, hp, TRUE, FALSE);
604 hp = NULL;
605 }
606 }
607 }
608
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100609 if (buf->b_ml.ml_stack_top == 0) // finished
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200610 break;
611
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100612 // go one block up in the tree
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200613 ip = &(buf->b_ml.ml_stack[--(buf->b_ml.ml_stack_top)]);
614 bnum = ip->ip_bnum;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100615 idx = ip->ip_index + 1; // go to next index
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200616 page_count = 1;
617 }
Bram Moolenaarbc563362015-06-09 18:35:25 +0200618 if (hp != NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100619 mf_put(mfp, hp, FALSE, FALSE); // release previous block
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +0100620
621 if (error > 0)
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +0000622 emsg(_(e_error_while_updating_swap_file_crypt));
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200623 }
624
625 mfp->mf_old_key = NULL;
626}
627#endif
628
Bram Moolenaar071d4272004-06-13 20:20:40 +0000629/*
630 * ml_setname() is called when the file name of "buf" has been changed.
631 * It may rename the swap file.
632 */
633 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100634ml_setname(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000635{
636 int success = FALSE;
637 memfile_T *mfp;
638 char_u *fname;
639 char_u *dirp;
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100640#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000641 char_u *p;
642#endif
643
644 mfp = buf->b_ml.ml_mfp;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100645 if (mfp->mf_fd < 0) // there is no swap file yet
Bram Moolenaar071d4272004-06-13 20:20:40 +0000646 {
647 /*
648 * When 'updatecount' is 0 and 'noswapfile' there is no swap file.
649 * For help files we will make a swap file now.
650 */
Bram Moolenaare1004402020-10-24 20:49:43 +0200651 if (p_uc != 0 && (cmdmod.cmod_flags & CMOD_NOSWAPFILE) == 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100652 ml_open_file(buf); // create a swap file
Bram Moolenaar071d4272004-06-13 20:20:40 +0000653 return;
654 }
655
656 /*
657 * Try all directories in the 'directory' option.
658 */
659 dirp = p_dir;
660 for (;;)
661 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100662 if (*dirp == NUL) // tried all directories, fail
Bram Moolenaar071d4272004-06-13 20:20:40 +0000663 break;
Bram Moolenaar8fc061c2004-12-29 21:03:02 +0000664 fname = findswapname(buf, &dirp, mfp->mf_fname);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100665 // alloc's fname
666 if (dirp == NULL) // out of memory
Bram Moolenaarf541c362011-10-26 11:44:18 +0200667 break;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100668 if (fname == NULL) // no file name found for this dir
Bram Moolenaar071d4272004-06-13 20:20:40 +0000669 continue;
670
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100671#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000672 /*
673 * Set full pathname for swap file now, because a ":!cd dir" may
674 * change directory without us knowing it.
675 */
676 p = FullName_save(fname, FALSE);
677 vim_free(fname);
678 fname = p;
679 if (fname == NULL)
680 continue;
681#endif
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100682 // if the file name is the same we don't have to do anything
Bram Moolenaar071d4272004-06-13 20:20:40 +0000683 if (fnamecmp(fname, mfp->mf_fname) == 0)
684 {
685 vim_free(fname);
686 success = TRUE;
687 break;
688 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100689 // need to close the swap file before renaming
Bram Moolenaar071d4272004-06-13 20:20:40 +0000690 if (mfp->mf_fd >= 0)
691 {
692 close(mfp->mf_fd);
693 mfp->mf_fd = -1;
694 }
695
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100696 // try to rename the swap file
Bram Moolenaar071d4272004-06-13 20:20:40 +0000697 if (vim_rename(mfp->mf_fname, fname) == 0)
698 {
699 success = TRUE;
700 vim_free(mfp->mf_fname);
701 mfp->mf_fname = fname;
702 vim_free(mfp->mf_ffname);
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100703#if defined(MSWIN)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100704 mfp->mf_ffname = NULL; // mf_fname is full pathname already
Bram Moolenaar071d4272004-06-13 20:20:40 +0000705#else
706 mf_set_ffname(mfp);
707#endif
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200708 ml_upd_block0(buf, UB_SAME_DIR);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000709 break;
710 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100711 vim_free(fname); // this fname didn't work, try another
Bram Moolenaar071d4272004-06-13 20:20:40 +0000712 }
713
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100714 if (mfp->mf_fd == -1) // need to (re)open the swap file
Bram Moolenaar071d4272004-06-13 20:20:40 +0000715 {
716 mfp->mf_fd = mch_open((char *)mfp->mf_fname, O_RDWR | O_EXTRA, 0);
717 if (mfp->mf_fd < 0)
718 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100719 // could not (re)open the swap file, what can we do????
Bram Moolenaareaaac012022-01-02 17:00:40 +0000720 emsg(_(e_oops_lost_the_swap_file));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000721 return;
722 }
Bram Moolenaarf05da212009-11-17 16:13:15 +0000723#ifdef HAVE_FD_CLOEXEC
724 {
725 int fdflags = fcntl(mfp->mf_fd, F_GETFD);
726 if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0)
Bram Moolenaarfbc4b4d2016-02-07 15:14:01 +0100727 (void)fcntl(mfp->mf_fd, F_SETFD, fdflags | FD_CLOEXEC);
Bram Moolenaarf05da212009-11-17 16:13:15 +0000728 }
729#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000730 }
731 if (!success)
Bram Moolenaareaaac012022-01-02 17:00:40 +0000732 emsg(_(e_could_not_rename_swap_file));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000733}
734
735/*
736 * Open a file for the memfile for all buffers that are not readonly or have
737 * been modified.
738 * Used when 'updatecount' changes from zero to non-zero.
739 */
740 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100741ml_open_files(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000742{
743 buf_T *buf;
744
Bram Moolenaar29323592016-07-24 22:04:11 +0200745 FOR_ALL_BUFFERS(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000746 if (!buf->b_p_ro || buf->b_changed)
747 ml_open_file(buf);
748}
749
750/*
751 * Open a swap file for an existing memfile, if there is no swap file yet.
752 * If we are unable to find a file name, mf_fname will be NULL
753 * and the memfile will be in memory only (no recovery possible).
754 */
755 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100756ml_open_file(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000757{
758 memfile_T *mfp;
759 char_u *fname;
760 char_u *dirp;
761
762 mfp = buf->b_ml.ml_mfp;
Bram Moolenaare1004402020-10-24 20:49:43 +0200763 if (mfp == NULL || mfp->mf_fd >= 0 || !buf->b_p_swf
764 || (cmdmod.cmod_flags & CMOD_NOSWAPFILE))
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100765 return; // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +0000766
Bram Moolenaara1956f62006-03-12 22:18:00 +0000767#ifdef FEAT_SPELL
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100768 // For a spell buffer use a temp file name.
Bram Moolenaar4770d092006-01-12 23:22:24 +0000769 if (buf->b_spell)
770 {
Bram Moolenaare5c421c2015-03-31 13:33:08 +0200771 fname = vim_tempname('s', FALSE);
Bram Moolenaar4770d092006-01-12 23:22:24 +0000772 if (fname != NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100773 (void)mf_open_file(mfp, fname); // consumes fname!
Bram Moolenaar4770d092006-01-12 23:22:24 +0000774 buf->b_may_swap = FALSE;
775 return;
776 }
777#endif
778
Bram Moolenaar071d4272004-06-13 20:20:40 +0000779 /*
780 * Try all directories in 'directory' option.
781 */
782 dirp = p_dir;
783 for (;;)
784 {
785 if (*dirp == NUL)
786 break;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100787 // There is a small chance that between choosing the swap file name
788 // and creating it, another Vim creates the file. In that case the
789 // creation will fail and we will use another directory.
790 fname = findswapname(buf, &dirp, NULL); // allocates fname
Bram Moolenaarf541c362011-10-26 11:44:18 +0200791 if (dirp == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100792 break; // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +0000793 if (fname == NULL)
794 continue;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100795 if (mf_open_file(mfp, fname) == OK) // consumes fname!
Bram Moolenaar071d4272004-06-13 20:20:40 +0000796 {
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100797#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000798 /*
799 * set full pathname for swap file now, because a ":!cd dir" may
800 * change directory without us knowing it.
801 */
802 mf_fullname(mfp);
803#endif
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200804 ml_upd_block0(buf, UB_SAME_DIR);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000805
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100806 // Flush block zero, so others can read it
Bram Moolenaar071d4272004-06-13 20:20:40 +0000807 if (mf_sync(mfp, MFS_ZERO) == OK)
Bram Moolenaarc32840f2006-01-14 21:23:38 +0000808 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100809 // Mark all blocks that should be in the swapfile as dirty.
810 // Needed for when the 'swapfile' option was reset, so that
811 // the swap file was deleted, and then on again.
Bram Moolenaarc32840f2006-01-14 21:23:38 +0000812 mf_set_dirty(mfp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000813 break;
Bram Moolenaarc32840f2006-01-14 21:23:38 +0000814 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100815 // Writing block 0 failed: close the file and try another dir
Bram Moolenaar071d4272004-06-13 20:20:40 +0000816 mf_close_file(buf, FALSE);
817 }
818 }
819
Bram Moolenaar00e192b2019-10-19 17:01:28 +0200820 if (*p_dir != NUL && mfp->mf_fname == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000821 {
Bram Moolenaar13608d82022-08-29 15:06:50 +0100822 need_wait_return = TRUE; // call wait_return() later
Bram Moolenaar071d4272004-06-13 20:20:40 +0000823 ++no_wait_return;
Bram Moolenaareaaac012022-01-02 17:00:40 +0000824 (void)semsg(_(e_unable_to_open_swap_file_for_str_recovery_impossible),
Bram Moolenaare1704ba2012-10-03 18:25:00 +0200825 buf_spname(buf) != NULL ? buf_spname(buf) : buf->b_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000826 --no_wait_return;
827 }
828
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100829 // don't try to open a swap file again
Bram Moolenaar071d4272004-06-13 20:20:40 +0000830 buf->b_may_swap = FALSE;
831}
832
833/*
834 * If still need to create a swap file, and starting to edit a not-readonly
835 * file, or reading into an existing buffer, create a swap file now.
836 */
837 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100838check_need_swap(
Bram Moolenaar2f0f8712018-08-21 18:50:18 +0200839 int newfile) // reading file into new buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +0000840{
Bram Moolenaar2f0f8712018-08-21 18:50:18 +0200841 int old_msg_silent = msg_silent; // might be reset by an E325 message
842
Bram Moolenaar071d4272004-06-13 20:20:40 +0000843 if (curbuf->b_may_swap && (!curbuf->b_p_ro || !newfile))
844 ml_open_file(curbuf);
Bram Moolenaar2f0f8712018-08-21 18:50:18 +0200845 msg_silent = old_msg_silent;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000846}
847
848/*
849 * Close memline for buffer 'buf'.
850 * If 'del_file' is TRUE, delete the swap file
851 */
852 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100853ml_close(buf_T *buf, int del_file)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000854{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100855 if (buf->b_ml.ml_mfp == NULL) // not open
Bram Moolenaar071d4272004-06-13 20:20:40 +0000856 return;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100857 mf_close(buf->b_ml.ml_mfp, del_file); // close the .swp file
Bram Moolenaarfa4873c2022-06-30 22:13:59 +0100858 if (buf->b_ml.ml_line_lnum != 0
859 && (buf->b_ml.ml_flags & (ML_LINE_DIRTY | ML_ALLOCATED)))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000860 vim_free(buf->b_ml.ml_line_ptr);
861 vim_free(buf->b_ml.ml_stack);
862#ifdef FEAT_BYTEOFF
Bram Moolenaard23a8232018-02-10 18:45:26 +0100863 VIM_CLEAR(buf->b_ml.ml_chunksize);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000864#endif
865 buf->b_ml.ml_mfp = NULL;
866
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100867 // Reset the "recovered" flag, give the ATTENTION prompt the next time
868 // this buffer is loaded.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000869 buf->b_flags &= ~BF_RECOVERED;
870}
871
872/*
873 * Close all existing memlines and memfiles.
874 * Only used when exiting.
875 * When 'del_file' is TRUE, delete the memfiles.
Bram Moolenaar81bf7082005-02-12 14:31:42 +0000876 * But don't delete files that were ":preserve"d when we are POSIX compatible.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000877 */
878 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100879ml_close_all(int del_file)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000880{
881 buf_T *buf;
882
Bram Moolenaar29323592016-07-24 22:04:11 +0200883 FOR_ALL_BUFFERS(buf)
Bram Moolenaar81bf7082005-02-12 14:31:42 +0000884 ml_close(buf, del_file && ((buf->b_flags & BF_PRESERVED) == 0
885 || vim_strchr(p_cpo, CPO_PRESERVE) == NULL));
Bram Moolenaar34b466e2013-11-28 17:41:46 +0100886#ifdef FEAT_SPELL
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100887 spell_delete_wordlist(); // delete the internal wordlist
Bram Moolenaar34b466e2013-11-28 17:41:46 +0100888#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000889#ifdef TEMPDIRNAMES
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100890 vim_deltempdir(); // delete created temp directory
Bram Moolenaar071d4272004-06-13 20:20:40 +0000891#endif
892}
893
894/*
895 * Close all memfiles for not modified buffers.
896 * Only use just before exiting!
897 */
898 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100899ml_close_notmod(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000900{
901 buf_T *buf;
902
Bram Moolenaar29323592016-07-24 22:04:11 +0200903 FOR_ALL_BUFFERS(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000904 if (!bufIsChanged(buf))
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100905 ml_close(buf, TRUE); // close all not-modified buffers
Bram Moolenaar071d4272004-06-13 20:20:40 +0000906}
907
908/*
909 * Update the timestamp in the .swp file.
910 * Used when the file has been written.
911 */
912 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100913ml_timestamp(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000914{
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200915 ml_upd_block0(buf, UB_FNAME);
916}
917
918/*
919 * Return FAIL when the ID of "b0p" is wrong.
920 */
921 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100922ml_check_b0_id(ZERO_BL *b0p)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200923{
924 if (b0p->b0_id[0] != BLOCK0_ID0
925 || (b0p->b0_id[1] != BLOCK0_ID1
926 && b0p->b0_id[1] != BLOCK0_ID1_C0
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200927 && b0p->b0_id[1] != BLOCK0_ID1_C1
Christian Brabandtf573c6e2021-06-20 14:02:16 +0200928 && b0p->b0_id[1] != BLOCK0_ID1_C2
929 && b0p->b0_id[1] != BLOCK0_ID1_C3)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200930 )
931 return FAIL;
932 return OK;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000933}
934
935/*
936 * Update the timestamp or the B0_SAME_DIR flag of the .swp file.
937 */
938 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100939ml_upd_block0(buf_T *buf, upd_block0_T what)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000940{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000941 memfile_T *mfp;
942 bhdr_T *hp;
943 ZERO_BL *b0p;
944
945 mfp = buf->b_ml.ml_mfp;
Bram Moolenaar2be79502014-08-13 21:58:28 +0200946 if (mfp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000947 return;
Bram Moolenaar2be79502014-08-13 21:58:28 +0200948 hp = mf_get(mfp, (blocknr_T)0, 1);
949 if (hp == NULL)
950 {
951#ifdef FEAT_CRYPT
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100952 // Possibly update the seed in the memfile before there is a block0.
Bram Moolenaar2be79502014-08-13 21:58:28 +0200953 if (what == UB_CRYPT)
954 ml_set_mfp_crypt(buf);
955#endif
956 return;
957 }
958
Bram Moolenaar071d4272004-06-13 20:20:40 +0000959 b0p = (ZERO_BL *)(hp->bh_data);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200960 if (ml_check_b0_id(b0p) == FAIL)
Bram Moolenaareaaac012022-01-02 17:00:40 +0000961 iemsg(_(e_ml_upd_block0_didnt_get_block_zero));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000962 else
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000963 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200964 if (what == UB_FNAME)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000965 set_b0_fname(b0p, buf);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200966#ifdef FEAT_CRYPT
967 else if (what == UB_CRYPT)
968 ml_set_b0_crypt(buf, b0p);
969#endif
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100970 else // what == UB_SAME_DIR
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000971 set_b0_dir_flag(b0p, buf);
972 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000973 mf_put(mfp, hp, TRUE, FALSE);
974}
975
976/*
977 * Write file name and timestamp into block 0 of a swap file.
978 * Also set buf->b_mtime.
979 * Don't use NameBuff[]!!!
980 */
981 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100982set_b0_fname(ZERO_BL *b0p, buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000983{
Bram Moolenaar8767f522016-07-01 17:17:39 +0200984 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000985
986 if (buf->b_ffname == NULL)
987 b0p->b0_fname[0] = NUL;
988 else
989 {
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100990#if defined(MSWIN) || defined(AMIGA)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100991 // Systems that cannot translate "~user" back into a path: copy the
992 // file name unmodified. Do use slashes instead of backslashes for
993 // portability.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200994 vim_strncpy(b0p->b0_fname, buf->b_ffname, B0_FNAME_SIZE_CRYPT - 1);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000995# ifdef BACKSLASH_IN_FILENAME
996 forward_slash(b0p->b0_fname);
997# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000998#else
999 size_t flen, ulen;
1000 char_u uname[B0_UNAME_SIZE];
1001
1002 /*
1003 * For a file under the home directory of the current user, we try to
1004 * replace the home directory path with "~user". This helps when
1005 * editing the same file on different machines over a network.
1006 * First replace home dir path with "~/" with home_replace().
1007 * Then insert the user name to get "~user/".
1008 */
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001009 home_replace(NULL, buf->b_ffname, b0p->b0_fname,
1010 B0_FNAME_SIZE_CRYPT, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001011 if (b0p->b0_fname[0] == '~')
1012 {
1013 flen = STRLEN(b0p->b0_fname);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001014 // If there is no user name or it is too long, don't use "~/"
Bram Moolenaar071d4272004-06-13 20:20:40 +00001015 if (get_user_name(uname, B0_UNAME_SIZE) == FAIL
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001016 || (ulen = STRLEN(uname)) + flen > B0_FNAME_SIZE_CRYPT - 1)
1017 vim_strncpy(b0p->b0_fname, buf->b_ffname,
1018 B0_FNAME_SIZE_CRYPT - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001019 else
1020 {
1021 mch_memmove(b0p->b0_fname + ulen + 1, b0p->b0_fname + 1, flen);
1022 mch_memmove(b0p->b0_fname + 1, uname, ulen);
1023 }
1024 }
1025#endif
1026 if (mch_stat((char *)buf->b_ffname, &st) >= 0)
1027 {
1028 long_to_char((long)st.st_mtime, b0p->b0_mtime);
1029#ifdef CHECK_INODE
1030 long_to_char((long)st.st_ino, b0p->b0_ino);
1031#endif
1032 buf_store_time(buf, &st, buf->b_ffname);
1033 buf->b_mtime_read = buf->b_mtime;
Leah Neukirchen0a7984a2021-10-14 21:27:55 +01001034 buf->b_mtime_read_ns = buf->b_mtime_ns;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001035 }
1036 else
1037 {
1038 long_to_char(0L, b0p->b0_mtime);
1039#ifdef CHECK_INODE
1040 long_to_char(0L, b0p->b0_ino);
1041#endif
1042 buf->b_mtime = 0;
Leah Neukirchen0a7984a2021-10-14 21:27:55 +01001043 buf->b_mtime_ns = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001044 buf->b_mtime_read = 0;
Leah Neukirchen0a7984a2021-10-14 21:27:55 +01001045 buf->b_mtime_read_ns = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001046 buf->b_orig_size = 0;
1047 buf->b_orig_mode = 0;
1048 }
1049 }
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001050
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001051 // Also add the 'fileencoding' if there is room.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001052 add_b0_fenc(b0p, curbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001053}
1054
1055/*
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001056 * Update the B0_SAME_DIR flag of the swap file. It's set if the file and the
1057 * swapfile for "buf" are in the same directory.
1058 * This is fail safe: if we are not sure the directories are equal the flag is
1059 * not set.
1060 */
1061 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001062set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001063{
1064 if (same_directory(buf->b_ml.ml_mfp->mf_fname, buf->b_ffname))
1065 b0p->b0_flags |= B0_SAME_DIR;
1066 else
1067 b0p->b0_flags &= ~B0_SAME_DIR;
1068}
1069
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001070/*
1071 * When there is room, add the 'fileencoding' to block zero.
1072 */
1073 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001074add_b0_fenc(
1075 ZERO_BL *b0p,
1076 buf_T *buf)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001077{
1078 int n;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001079 int size = B0_FNAME_SIZE_NOCRYPT;
1080
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01001081#ifdef FEAT_CRYPT
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001082 // Without encryption use the same offset as in Vim 7.2 to be compatible.
1083 // With encryption it's OK to move elsewhere, the swap file is not
1084 // compatible anyway.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001085 if (*buf->b_p_key != NUL)
1086 size = B0_FNAME_SIZE_CRYPT;
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01001087#endif
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001088
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00001089 n = (int)STRLEN(buf->b_p_fenc);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001090 if ((int)STRLEN(b0p->b0_fname) + n + 1 > size)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001091 b0p->b0_flags &= ~B0_HAS_FENC;
1092 else
1093 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001094 mch_memmove((char *)b0p->b0_fname + size - n,
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001095 (char *)buf->b_p_fenc, (size_t)n);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001096 *(b0p->b0_fname + size - n - 1) = NUL;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001097 b0p->b0_flags |= B0_HAS_FENC;
1098 }
1099}
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001100
Bram Moolenaarf52f0602021-03-10 21:26:37 +01001101#if defined(HAVE_SYS_SYSINFO_H) && defined(HAVE_SYSINFO_UPTIME)
1102# include <sys/sysinfo.h>
1103#endif
1104
=?UTF-8?q?Ola=20S=C3=B6der?=599a6e52021-07-06 20:15:46 +02001105#if defined(UNIX) || defined(MSWIN)
Bram Moolenaarf52f0602021-03-10 21:26:37 +01001106/*
1107 * Return TRUE if the process with number "b0p->b0_pid" is still running.
1108 * "swap_fname" is the name of the swap file, if it's from before a reboot then
1109 * the result is FALSE;
1110 */
1111 static int
1112swapfile_process_running(ZERO_BL *b0p, char_u *swap_fname UNUSED)
1113{
Bram Moolenaare2982d62021-10-06 11:27:21 +01001114#if defined(HAVE_SYSINFO) && defined(HAVE_SYSINFO_UPTIME)
Bram Moolenaarf52f0602021-03-10 21:26:37 +01001115 stat_T st;
1116 struct sysinfo sinfo;
1117
1118 // If the system rebooted after when the swap file was written then the
1119 // process can't be running now.
1120 if (mch_stat((char *)swap_fname, &st) != -1
1121 && sysinfo(&sinfo) == 0
Bram Moolenaar23b32a82021-03-10 21:55:46 +01001122 && st.st_mtime < time(NULL) - (
=?UTF-8?q?Ola=20S=C3=B6der?=599a6e52021-07-06 20:15:46 +02001123# ifdef FEAT_EVAL
Bram Moolenaar23b32a82021-03-10 21:55:46 +01001124 override_sysinfo_uptime >= 0 ? override_sysinfo_uptime :
=?UTF-8?q?Ola=20S=C3=B6der?=599a6e52021-07-06 20:15:46 +02001125# endif
Bram Moolenaar23b32a82021-03-10 21:55:46 +01001126 sinfo.uptime))
Bram Moolenaarf52f0602021-03-10 21:26:37 +01001127 return FALSE;
=?UTF-8?q?Ola=20S=C3=B6der?=599a6e52021-07-06 20:15:46 +02001128# endif
Bram Moolenaarf52f0602021-03-10 21:26:37 +01001129 return mch_process_running(char_to_long(b0p->b0_pid));
1130}
=?UTF-8?q?Ola=20S=C3=B6der?=599a6e52021-07-06 20:15:46 +02001131#endif
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001132
1133/*
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001134 * Try to recover curbuf from the .swp file.
Bram Moolenaar99499b12019-05-23 21:35:48 +02001135 * If "checkext" is TRUE, check the extension and detect whether it is
1136 * a swap file.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001137 */
1138 void
Bram Moolenaar99499b12019-05-23 21:35:48 +02001139ml_recover(int checkext)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001140{
1141 buf_T *buf = NULL;
1142 memfile_T *mfp = NULL;
1143 char_u *fname;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001144 char_u *fname_used = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001145 bhdr_T *hp = NULL;
1146 ZERO_BL *b0p;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001147 int b0_ff;
1148 char_u *b0_fenc = NULL;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001149#ifdef FEAT_CRYPT
1150 int b0_cm = -1;
1151#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001152 PTR_BL *pp;
1153 DATA_BL *dp;
1154 infoptr_T *ip;
1155 blocknr_T bnum;
1156 int page_count;
Bram Moolenaar8767f522016-07-01 17:17:39 +02001157 stat_T org_stat, swp_stat;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001158 int len;
1159 int directly;
1160 linenr_T lnum;
1161 char_u *p;
1162 int i;
1163 long error;
1164 int cannot_open;
1165 linenr_T line_count;
1166 int has_error;
1167 int idx;
1168 int top;
1169 int txt_start;
Bram Moolenaar8767f522016-07-01 17:17:39 +02001170 off_T size;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001171 int called_from_main;
1172 int serious_error = TRUE;
1173 long mtime;
1174 int attr;
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001175 int orig_file_status = NOTDONE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001176
1177 recoverymode = TRUE;
1178 called_from_main = (curbuf->b_ml.ml_mfp == NULL);
Bram Moolenaar8820b482017-03-16 17:23:31 +01001179 attr = HL_ATTR(HLF_E);
Bram Moolenaard0ba34a2009-11-03 12:06:23 +00001180
1181 /*
Bram Moolenaaraf903e52017-12-02 15:11:22 +01001182 * If the file name ends in ".s[a-w][a-z]" we assume this is the swap file.
Bram Moolenaard0ba34a2009-11-03 12:06:23 +00001183 * Otherwise a search is done to find the swap file(s).
1184 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001185 fname = curbuf->b_fname;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001186 if (fname == NULL) // When there is no file name
Bram Moolenaar071d4272004-06-13 20:20:40 +00001187 fname = (char_u *)"";
1188 len = (int)STRLEN(fname);
Bram Moolenaar99499b12019-05-23 21:35:48 +02001189 if (checkext && len >= 4 &&
Bram Moolenaare60acc12011-05-10 16:41:25 +02001190#if defined(VMS)
Bram Moolenaar79518e22017-02-17 16:31:35 +01001191 STRNICMP(fname + len - 4, "_s", 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001192#else
Bram Moolenaar79518e22017-02-17 16:31:35 +01001193 STRNICMP(fname + len - 4, ".s", 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001194#endif
Bram Moolenaar79518e22017-02-17 16:31:35 +01001195 == 0
Bram Moolenaaraf903e52017-12-02 15:11:22 +01001196 && vim_strchr((char_u *)"abcdefghijklmnopqrstuvw",
1197 TOLOWER_ASC(fname[len - 2])) != NULL
Bram Moolenaard0ba34a2009-11-03 12:06:23 +00001198 && ASCII_ISALPHA(fname[len - 1]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001199 {
1200 directly = TRUE;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001201 fname_used = vim_strsave(fname); // make a copy for mf_open()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001202 }
1203 else
1204 {
1205 directly = FALSE;
1206
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001207 // count the number of matching swap files
Bram Moolenaarc216a7a2022-12-05 13:50:55 +00001208 len = recover_names(fname, FALSE, NULL, 0, NULL);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001209 if (len == 0) // no swap files found
Bram Moolenaar071d4272004-06-13 20:20:40 +00001210 {
Bram Moolenaareaaac012022-01-02 17:00:40 +00001211 semsg(_(e_no_swap_file_found_for_str), fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001212 goto theend;
1213 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001214 if (len == 1) // one swap file found, use it
Bram Moolenaar071d4272004-06-13 20:20:40 +00001215 i = 1;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001216 else // several swap files found, choose
Bram Moolenaar071d4272004-06-13 20:20:40 +00001217 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001218 // list the names of the swap files
Bram Moolenaarc216a7a2022-12-05 13:50:55 +00001219 (void)recover_names(fname, TRUE, NULL, 0, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001220 msg_putchar('\n');
Bram Moolenaar32526b32019-01-19 17:43:09 +01001221 msg_puts(_("Enter number of swap file to use (0 to quit): "));
Bram Moolenaar24bbcfe2005-06-28 23:32:02 +00001222 i = get_number(FALSE, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001223 if (i < 1 || i > len)
1224 goto theend;
1225 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001226 // get the swap file name that will be used
Bram Moolenaarc216a7a2022-12-05 13:50:55 +00001227 (void)recover_names(fname, FALSE, NULL, i, &fname_used);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001228 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001229 if (fname_used == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001230 goto theend; // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00001231
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001232 // When called from main() still need to initialize storage structure
Bram Moolenaar4770d092006-01-12 23:22:24 +00001233 if (called_from_main && ml_open(curbuf) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001234 getout(1);
1235
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001236 /*
1237 * Allocate a buffer structure for the swap file that is used for recovery.
Bram Moolenaar0ad014c2010-07-25 14:00:46 +02001238 * Only the memline and crypt information in it are really used.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001239 */
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001240 buf = ALLOC_ONE(buf_T);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001241 if (buf == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001242 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001243
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001244 /*
1245 * init fields in memline struct
1246 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001247 buf->b_ml.ml_stack_size = 0; // no stack yet
1248 buf->b_ml.ml_stack = NULL; // no stack yet
1249 buf->b_ml.ml_stack_top = 0; // nothing in the stack
1250 buf->b_ml.ml_line_lnum = 0; // no cached line
1251 buf->b_ml.ml_locked = NULL; // no locked block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001252 buf->b_ml.ml_flags = 0;
Bram Moolenaar0fe849a2010-07-25 15:11:11 +02001253#ifdef FEAT_CRYPT
1254 buf->b_p_key = empty_option;
1255 buf->b_p_cm = empty_option;
1256#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001257
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001258 /*
1259 * open the memfile from the old swap file
1260 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001261 p = vim_strsave(fname_used); // save "fname_used" for the message:
1262 // mf_open() will consume "fname_used"!
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001263 mfp = mf_open(fname_used, O_RDONLY);
1264 fname_used = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001265 if (mfp == NULL || mfp->mf_fd < 0)
1266 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001267 if (fname_used != NULL)
Bram Moolenaareaaac012022-01-02 17:00:40 +00001268 semsg(_(e_cannot_open_str), fname_used);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001269 goto theend;
1270 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001271 buf->b_ml.ml_mfp = mfp;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001272#ifdef FEAT_CRYPT
1273 mfp->mf_buffer = buf;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001274#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001275
1276 /*
1277 * The page size set in mf_open() might be different from the page size
1278 * used in the swap file, we must get it from block 0. But to read block
1279 * 0 we need a page size. Use the minimal size for block 0 here, it will
1280 * be set to the real value below.
1281 */
1282 mfp->mf_page_size = MIN_SWAP_PAGE_SIZE;
1283
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001284 /*
1285 * try to read block 0
1286 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001287 if ((hp = mf_get(mfp, (blocknr_T)0, 1)) == NULL)
1288 {
1289 msg_start();
Bram Moolenaar32526b32019-01-19 17:43:09 +01001290 msg_puts_attr(_("Unable to read block 0 from "), attr | MSG_HIST);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001291 msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001292 msg_puts_attr(_("\nMaybe no changes were made or Vim did not update the swap file."),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001293 attr | MSG_HIST);
1294 msg_end();
1295 goto theend;
1296 }
1297 b0p = (ZERO_BL *)(hp->bh_data);
1298 if (STRNCMP(b0p->b0_version, "VIM 3.0", 7) == 0)
1299 {
1300 msg_start();
1301 msg_outtrans_attr(mfp->mf_fname, MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001302 msg_puts_attr(_(" cannot be used with this version of Vim.\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001303 MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001304 msg_puts_attr(_("Use Vim version 3.0.\n"), MSG_HIST);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001305 msg_end();
1306 goto theend;
1307 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001308 if (ml_check_b0_id(b0p) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001309 {
Bram Moolenaareaaac012022-01-02 17:00:40 +00001310 semsg(_(e_str_does_not_look_like_vim_swap_file), mfp->mf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001311 goto theend;
1312 }
1313 if (b0_magic_wrong(b0p))
1314 {
1315 msg_start();
1316 msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
Bram Moolenaar48e330a2016-02-23 14:53:34 +01001317#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001318 if (STRNCMP(b0p->b0_hname, "PC ", 3) == 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001319 msg_puts_attr(_(" cannot be used with this version of Vim.\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001320 attr | MSG_HIST);
1321 else
1322#endif
Bram Moolenaar32526b32019-01-19 17:43:09 +01001323 msg_puts_attr(_(" cannot be used on this computer.\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001324 attr | MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001325 msg_puts_attr(_("The file was created on "), attr | MSG_HIST);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001326 // avoid going past the end of a corrupted hostname
Bram Moolenaar071d4272004-06-13 20:20:40 +00001327 b0p->b0_fname[0] = NUL;
Bram Moolenaar32526b32019-01-19 17:43:09 +01001328 msg_puts_attr((char *)b0p->b0_hname, attr | MSG_HIST);
1329 msg_puts_attr(_(",\nor the file has been damaged."), attr | MSG_HIST);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001330 msg_end();
1331 goto theend;
1332 }
Bram Moolenaar1c536282007-04-26 15:21:56 +00001333
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001334#ifdef FEAT_CRYPT
K.Takataeeec2542021-06-02 13:28:16 +02001335 for (i = 0; i < (int)ARRAY_LENGTH(id1_codes); ++i)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001336 if (id1_codes[i] == b0p->b0_id[1])
1337 b0_cm = i;
1338 if (b0_cm > 0)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001339 mch_memmove(mfp->mf_seed, &b0p->b0_seed, MF_SEED_LEN);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001340 crypt_set_cm_option(buf, b0_cm < 0 ? 0 : b0_cm);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001341#else
1342 if (b0p->b0_id[1] != BLOCK0_ID1)
1343 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00001344 semsg(_(e_str_is_encrypted_and_this_version_of_vim_does_not_support_encryption), mfp->mf_fname);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001345 goto theend;
1346 }
1347#endif
1348
Bram Moolenaar071d4272004-06-13 20:20:40 +00001349 /*
1350 * If we guessed the wrong page size, we have to recalculate the
1351 * highest block number in the file.
1352 */
1353 if (mfp->mf_page_size != (unsigned)char_to_long(b0p->b0_page_size))
1354 {
Bram Moolenaar1c536282007-04-26 15:21:56 +00001355 unsigned previous_page_size = mfp->mf_page_size;
1356
Bram Moolenaar071d4272004-06-13 20:20:40 +00001357 mf_new_page_size(mfp, (unsigned)char_to_long(b0p->b0_page_size));
Bram Moolenaar1c536282007-04-26 15:21:56 +00001358 if (mfp->mf_page_size < previous_page_size)
1359 {
1360 msg_start();
1361 msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001362 msg_puts_attr(_(" has been damaged (page size is smaller than minimum value).\n"),
Bram Moolenaar1c536282007-04-26 15:21:56 +00001363 attr | MSG_HIST);
1364 msg_end();
1365 goto theend;
1366 }
Bram Moolenaar8767f522016-07-01 17:17:39 +02001367 if ((size = vim_lseek(mfp->mf_fd, (off_T)0L, SEEK_END)) <= 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001368 mfp->mf_blocknr_max = 0; // no file or empty file
Bram Moolenaar071d4272004-06-13 20:20:40 +00001369 else
1370 mfp->mf_blocknr_max = (blocknr_T)(size / mfp->mf_page_size);
1371 mfp->mf_infile_count = mfp->mf_blocknr_max;
Bram Moolenaar1c536282007-04-26 15:21:56 +00001372
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001373 // need to reallocate the memory used to store the data
Bram Moolenaar1c536282007-04-26 15:21:56 +00001374 p = alloc(mfp->mf_page_size);
1375 if (p == NULL)
1376 goto theend;
1377 mch_memmove(p, hp->bh_data, previous_page_size);
1378 vim_free(hp->bh_data);
1379 hp->bh_data = p;
1380 b0p = (ZERO_BL *)(hp->bh_data);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001381 }
1382
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001383 /*
1384 * If .swp file name given directly, use name from swap file for buffer.
1385 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001386 if (directly)
1387 {
1388 expand_env(b0p->b0_fname, NameBuff, MAXPATHL);
1389 if (setfname(curbuf, NameBuff, NULL, TRUE) == FAIL)
1390 goto theend;
1391 }
1392
1393 home_replace(NULL, mfp->mf_fname, NameBuff, MAXPATHL, TRUE);
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001394 smsg(_("Using swap file \"%s\""), NameBuff);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001395
1396 if (buf_spname(curbuf) != NULL)
Bram Moolenaare1704ba2012-10-03 18:25:00 +02001397 vim_strncpy(NameBuff, buf_spname(curbuf), MAXPATHL - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001398 else
1399 home_replace(NULL, curbuf->b_ffname, NameBuff, MAXPATHL, TRUE);
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001400 smsg(_("Original file \"%s\""), NameBuff);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001401 msg_putchar('\n');
1402
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001403 /*
1404 * check date of swap file and original file
1405 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001406 mtime = char_to_long(b0p->b0_mtime);
1407 if (curbuf->b_ffname != NULL
1408 && mch_stat((char *)curbuf->b_ffname, &org_stat) != -1
1409 && ((mch_stat((char *)mfp->mf_fname, &swp_stat) != -1
1410 && org_stat.st_mtime > swp_stat.st_mtime)
1411 || org_stat.st_mtime != mtime))
Bram Moolenaareaaac012022-01-02 17:00:40 +00001412 emsg(_(e_warning_original_file_may_have_been_changed));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001413 out_flush();
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001414
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001415 // Get the 'fileformat' and 'fileencoding' from block zero.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001416 b0_ff = (b0p->b0_flags & B0_FF_MASK);
1417 if (b0p->b0_flags & B0_HAS_FENC)
1418 {
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001419 int fnsize = B0_FNAME_SIZE_NOCRYPT;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001420
1421#ifdef FEAT_CRYPT
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001422 // Use the same size as in add_b0_fenc().
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001423 if (b0p->b0_id[1] != BLOCK0_ID1)
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001424 fnsize = B0_FNAME_SIZE_CRYPT;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001425#endif
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001426 for (p = b0p->b0_fname + fnsize; p > b0p->b0_fname && p[-1] != NUL; --p)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001427 ;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001428 b0_fenc = vim_strnsave(p, b0p->b0_fname + fnsize - p);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001429 }
1430
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001431 mf_put(mfp, hp, FALSE, FALSE); // release block 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001432 hp = NULL;
1433
1434 /*
1435 * Now that we are sure that the file is going to be recovered, clear the
1436 * contents of the current buffer.
1437 */
1438 while (!(curbuf->b_ml.ml_flags & ML_EMPTY))
Bram Moolenaarca70c072020-05-30 20:30:46 +02001439 ml_delete((linenr_T)1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001440
1441 /*
1442 * Try reading the original file to obtain the values of 'fileformat',
1443 * 'fileencoding', etc. Ignore errors. The text itself is not used.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001444 * When the file is encrypted the user is asked to enter the key.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001445 */
1446 if (curbuf->b_ffname != NULL)
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001447 orig_file_status = readfile(curbuf->b_ffname, NULL, (linenr_T)0,
Bram Moolenaar071d4272004-06-13 20:20:40 +00001448 (linenr_T)0, (linenr_T)MAXLNUM, NULL, READ_NEW);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001449
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001450#ifdef FEAT_CRYPT
1451 if (b0_cm >= 0)
1452 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001453 // Need to ask the user for the crypt key. If this fails we continue
1454 // without a key, will probably get garbage text.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001455 if (*curbuf->b_p_key != NUL)
1456 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001457 smsg(_("Swap file is encrypted: \"%s\""), fname_used);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001458 msg_puts(_("\nIf you entered a new crypt key but did not write the text file,"));
1459 msg_puts(_("\nenter the new crypt key."));
1460 msg_puts(_("\nIf you wrote the text file after changing the crypt key press enter"));
1461 msg_puts(_("\nto use the same key for text file and swap file"));
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001462 }
1463 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001464 smsg(_(need_key_msg), fname_used);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001465 buf->b_p_key = crypt_get_key(FALSE, FALSE);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001466 if (buf->b_p_key == NULL)
1467 buf->b_p_key = curbuf->b_p_key;
1468 else if (*buf->b_p_key == NUL)
1469 {
1470 vim_free(buf->b_p_key);
1471 buf->b_p_key = curbuf->b_p_key;
1472 }
1473 if (buf->b_p_key == NULL)
1474 buf->b_p_key = empty_option;
1475 }
1476#endif
1477
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001478 // Use the 'fileformat' and 'fileencoding' as stored in the swap file.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001479 if (b0_ff != 0)
1480 set_fileformat(b0_ff - 1, OPT_LOCAL);
1481 if (b0_fenc != NULL)
1482 {
Bram Moolenaar31e5c602022-04-15 13:53:33 +01001483 set_option_value_give_err((char_u *)"fenc", 0L, b0_fenc, OPT_LOCAL);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001484 vim_free(b0_fenc);
1485 }
Bram Moolenaarc024b462019-06-08 18:07:21 +02001486 unchanged(curbuf, TRUE, TRUE);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001487
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001488 bnum = 1; // start with block 1
1489 page_count = 1; // which is 1 page
1490 lnum = 0; // append after line 0 in curbuf
Bram Moolenaar071d4272004-06-13 20:20:40 +00001491 line_count = 0;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001492 idx = 0; // start with first index in block 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00001493 error = 0;
1494 buf->b_ml.ml_stack_top = 0;
1495 buf->b_ml.ml_stack = NULL;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001496 buf->b_ml.ml_stack_size = 0; // no stack yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00001497
1498 if (curbuf->b_ffname == NULL)
1499 cannot_open = TRUE;
1500 else
1501 cannot_open = FALSE;
1502
1503 serious_error = FALSE;
1504 for ( ; !got_int; line_breakcheck())
1505 {
1506 if (hp != NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001507 mf_put(mfp, hp, FALSE, FALSE); // release previous block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001508
1509 /*
1510 * get block
1511 */
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00001512 if ((hp = mf_get(mfp, bnum, page_count)) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001513 {
1514 if (bnum == 1)
1515 {
Bram Moolenaareaaac012022-01-02 17:00:40 +00001516 semsg(_(e_unable_to_read_block_one_from_str), mfp->mf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001517 goto theend;
1518 }
1519 ++error;
1520 ml_append(lnum++, (char_u *)_("???MANY LINES MISSING"),
1521 (colnr_T)0, TRUE);
1522 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001523 else // there is a block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001524 {
1525 pp = (PTR_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001526 if (pp->pb_id == PTR_ID) // it is a pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001527 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001528 // check line count when using pointer block first time
Bram Moolenaar071d4272004-06-13 20:20:40 +00001529 if (idx == 0 && line_count != 0)
1530 {
1531 for (i = 0; i < (int)pp->pb_count; ++i)
1532 line_count -= pp->pb_pointer[i].pe_line_count;
1533 if (line_count != 0)
1534 {
1535 ++error;
1536 ml_append(lnum++, (char_u *)_("???LINE COUNT WRONG"),
1537 (colnr_T)0, TRUE);
1538 }
1539 }
1540
1541 if (pp->pb_count == 0)
1542 {
1543 ml_append(lnum++, (char_u *)_("???EMPTY BLOCK"),
1544 (colnr_T)0, TRUE);
1545 ++error;
1546 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001547 else if (idx < (int)pp->pb_count) // go a block deeper
Bram Moolenaar071d4272004-06-13 20:20:40 +00001548 {
1549 if (pp->pb_pointer[idx].pe_bnum < 0)
1550 {
1551 /*
1552 * Data block with negative block number.
1553 * Try to read lines from the original file.
1554 * This is slow, but it works.
1555 */
1556 if (!cannot_open)
1557 {
1558 line_count = pp->pb_pointer[idx].pe_line_count;
1559 if (readfile(curbuf->b_ffname, NULL, lnum,
1560 pp->pb_pointer[idx].pe_old_lnum - 1,
Bram Moolenaare13b9af2017-01-13 22:01:02 +01001561 line_count, NULL, 0) != OK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001562 cannot_open = TRUE;
1563 else
1564 lnum += line_count;
1565 }
1566 if (cannot_open)
1567 {
1568 ++error;
1569 ml_append(lnum++, (char_u *)_("???LINES MISSING"),
1570 (colnr_T)0, TRUE);
1571 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001572 ++idx; // get same block again for next index
Bram Moolenaar071d4272004-06-13 20:20:40 +00001573 continue;
1574 }
1575
1576 /*
1577 * going one block deeper in the tree
1578 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001579 if ((top = ml_add_stack(buf)) < 0) // new entry in stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00001580 {
1581 ++error;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001582 break; // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00001583 }
1584 ip = &(buf->b_ml.ml_stack[top]);
1585 ip->ip_bnum = bnum;
1586 ip->ip_index = idx;
1587
1588 bnum = pp->pb_pointer[idx].pe_bnum;
1589 line_count = pp->pb_pointer[idx].pe_line_count;
1590 page_count = pp->pb_pointer[idx].pe_page_count;
Bram Moolenaar986a0032011-06-13 01:07:27 +02001591 idx = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001592 continue;
1593 }
1594 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001595 else // not a pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001596 {
1597 dp = (DATA_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001598 if (dp->db_id != DATA_ID) // block id wrong
Bram Moolenaar071d4272004-06-13 20:20:40 +00001599 {
1600 if (bnum == 1)
1601 {
Bram Moolenaareaaac012022-01-02 17:00:40 +00001602 semsg(_(e_block_one_id_wrong_str_not_swp_file),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001603 mfp->mf_fname);
1604 goto theend;
1605 }
1606 ++error;
1607 ml_append(lnum++, (char_u *)_("???BLOCK MISSING"),
1608 (colnr_T)0, TRUE);
1609 }
1610 else
1611 {
1612 /*
1613 * it is a data block
1614 * Append all the lines in this block
1615 */
1616 has_error = FALSE;
1617 /*
1618 * check length of block
1619 * if wrong, use length in pointer block
1620 */
1621 if (page_count * mfp->mf_page_size != dp->db_txt_end)
1622 {
1623 ml_append(lnum++, (char_u *)_("??? from here until ???END lines may be messed up"),
1624 (colnr_T)0, TRUE);
1625 ++error;
1626 has_error = TRUE;
1627 dp->db_txt_end = page_count * mfp->mf_page_size;
1628 }
1629
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001630 // make sure there is a NUL at the end of the block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001631 *((char_u *)dp + dp->db_txt_end - 1) = NUL;
1632
1633 /*
1634 * check number of lines in block
1635 * if wrong, use count in data block
1636 */
1637 if (line_count != dp->db_line_count)
1638 {
1639 ml_append(lnum++, (char_u *)_("??? from here until ???END lines may have been inserted/deleted"),
1640 (colnr_T)0, TRUE);
1641 ++error;
1642 has_error = TRUE;
1643 }
1644
1645 for (i = 0; i < dp->db_line_count; ++i)
1646 {
1647 txt_start = (dp->db_index[i] & DB_INDEX_MASK);
Bram Moolenaar740885b2009-11-03 14:33:17 +00001648 if (txt_start <= (int)HEADER_SIZE
Bram Moolenaar071d4272004-06-13 20:20:40 +00001649 || txt_start >= (int)dp->db_txt_end)
1650 {
1651 p = (char_u *)"???";
1652 ++error;
1653 }
1654 else
1655 p = (char_u *)dp + txt_start;
1656 ml_append(lnum++, p, (colnr_T)0, TRUE);
1657 }
1658 if (has_error)
Bram Moolenaar740885b2009-11-03 14:33:17 +00001659 ml_append(lnum++, (char_u *)_("???END"),
1660 (colnr_T)0, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001661 }
1662 }
1663 }
1664
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001665 if (buf->b_ml.ml_stack_top == 0) // finished
Bram Moolenaar071d4272004-06-13 20:20:40 +00001666 break;
1667
1668 /*
1669 * go one block up in the tree
1670 */
1671 ip = &(buf->b_ml.ml_stack[--(buf->b_ml.ml_stack_top)]);
1672 bnum = ip->ip_bnum;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001673 idx = ip->ip_index + 1; // go to next index
Bram Moolenaar071d4272004-06-13 20:20:40 +00001674 page_count = 1;
1675 }
1676
1677 /*
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001678 * Compare the buffer contents with the original file. When they differ
1679 * set the 'modified' flag.
1680 * Lines 1 - lnum are the new contents.
1681 * Lines lnum + 1 to ml_line_count are the original contents.
1682 * Line ml_line_count + 1 in the dummy empty line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001683 */
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001684 if (orig_file_status != OK || curbuf->b_ml.ml_line_count != lnum * 2 + 1)
1685 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001686 // Recovering an empty file results in two lines and the first line is
1687 // empty. Don't set the modified flag then.
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001688 if (!(curbuf->b_ml.ml_line_count == 2 && *ml_get(1) == NUL))
1689 {
Bram Moolenaarec28d152019-05-11 18:36:34 +02001690 changed_internal();
Bram Moolenaar95c526e2017-02-25 14:59:34 +01001691 ++CHANGEDTICK(curbuf);
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001692 }
1693 }
1694 else
1695 {
1696 for (idx = 1; idx <= lnum; ++idx)
1697 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001698 // Need to copy one line, fetching the other one may flush it.
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001699 p = vim_strsave(ml_get(idx));
1700 i = STRCMP(p, ml_get(idx + lnum));
1701 vim_free(p);
1702 if (i != 0)
1703 {
Bram Moolenaarec28d152019-05-11 18:36:34 +02001704 changed_internal();
Bram Moolenaar95c526e2017-02-25 14:59:34 +01001705 ++CHANGEDTICK(curbuf);
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001706 break;
1707 }
1708 }
1709 }
1710
1711 /*
1712 * Delete the lines from the original file and the dummy line from the
1713 * empty buffer. These will now be after the last line in the buffer.
1714 */
1715 while (curbuf->b_ml.ml_line_count > lnum
1716 && !(curbuf->b_ml.ml_flags & ML_EMPTY))
Bram Moolenaarca70c072020-05-30 20:30:46 +02001717 ml_delete(curbuf->b_ml.ml_line_count);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001718 curbuf->b_flags |= BF_RECOVERED;
Bram Moolenaare3f50ad2021-06-09 12:33:40 +02001719 check_cursor();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001720
1721 recoverymode = FALSE;
1722 if (got_int)
Bram Moolenaareaaac012022-01-02 17:00:40 +00001723 emsg(_(e_recovery_interrupted));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001724 else if (error)
1725 {
1726 ++no_wait_return;
Bram Moolenaar32526b32019-01-19 17:43:09 +01001727 msg(">>>>>>>>>>>>>");
Bram Moolenaareaaac012022-01-02 17:00:40 +00001728 emsg(_(e_errors_detected_while_recovering_look_for_lines_starting_with_questions));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001729 --no_wait_return;
Bram Moolenaar32526b32019-01-19 17:43:09 +01001730 msg(_("See \":help E312\" for more information."));
1731 msg(">>>>>>>>>>>>>");
Bram Moolenaar071d4272004-06-13 20:20:40 +00001732 }
1733 else
1734 {
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001735 if (curbuf->b_changed)
1736 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001737 msg(_("Recovery completed. You should check if everything is OK."));
1738 msg_puts(_("\n(You might want to write out this file under another name\n"));
1739 msg_puts(_("and run diff with the original file to check for changes)"));
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001740 }
1741 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001742 msg(_("Recovery completed. Buffer contents equals file contents."));
Bram Moolenaarf8835082020-11-09 21:04:17 +01001743 msg_puts(_("\nYou may want to delete the .swp file now."));
1744#if defined(UNIX) || defined(MSWIN)
Bram Moolenaarf52f0602021-03-10 21:26:37 +01001745 if (swapfile_process_running(b0p, fname_used))
Bram Moolenaarf8835082020-11-09 21:04:17 +01001746 {
1747 // Warn there could be an active Vim on the same file, the user may
1748 // want to kill it.
1749 msg_puts(_("\nNote: process STILL RUNNING: "));
1750 msg_outnum(char_to_long(b0p->b0_pid));
1751 }
1752#endif
1753 msg_puts("\n\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00001754 cmdline_row = msg_row;
1755 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001756#ifdef FEAT_CRYPT
1757 if (*buf->b_p_key != NUL && STRCMP(curbuf->b_p_key, buf->b_p_key) != 0)
1758 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001759 msg_puts(_("Using crypt key from swap file for the text file.\n"));
Bram Moolenaar31e5c602022-04-15 13:53:33 +01001760 set_option_value_give_err((char_u *)"key", 0L, buf->b_p_key, OPT_LOCAL);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001761 }
1762#endif
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001763 redraw_curbuf_later(UPD_NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001764
1765theend:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001766 vim_free(fname_used);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001767 recoverymode = FALSE;
1768 if (mfp != NULL)
1769 {
1770 if (hp != NULL)
1771 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001772 mf_close(mfp, FALSE); // will also vim_free(mfp->mf_fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001773 }
Bram Moolenaardf88dda2007-01-09 13:34:50 +00001774 if (buf != NULL)
1775 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001776#ifdef FEAT_CRYPT
1777 if (buf->b_p_key != curbuf->b_p_key)
1778 free_string_option(buf->b_p_key);
Bram Moolenaar0ad014c2010-07-25 14:00:46 +02001779 free_string_option(buf->b_p_cm);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001780#endif
Bram Moolenaardf88dda2007-01-09 13:34:50 +00001781 vim_free(buf->b_ml.ml_stack);
1782 vim_free(buf);
1783 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001784 if (serious_error && called_from_main)
1785 ml_close(curbuf, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001786 else
1787 {
1788 apply_autocmds(EVENT_BUFREADPOST, NULL, curbuf->b_fname, FALSE, curbuf);
1789 apply_autocmds(EVENT_BUFWINENTER, NULL, curbuf->b_fname, FALSE, curbuf);
1790 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001791}
1792
1793/*
1794 * Find the names of swap files in current directory and the directory given
1795 * with the 'directory' option.
1796 *
1797 * Used to:
1798 * - list the swap files for "vim -r"
1799 * - count the number of swap files when recovering
1800 * - list the swap files when recovering
Bram Moolenaarc216a7a2022-12-05 13:50:55 +00001801 * - list the swap files for swapfilelist()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001802 * - find the name of the n'th swap file when recovering
1803 */
1804 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001805recover_names(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001806 char_u *fname, // base for swap file name
Bram Moolenaarc216a7a2022-12-05 13:50:55 +00001807 int do_list, // when TRUE, list the swap file names
1808 list_T *ret_list UNUSED, // when not NULL add file names to it
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001809 int nr, // when non-zero, return nr'th swap file name
1810 char_u **fname_out) // result when "nr" > 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001811{
1812 int num_names;
1813 char_u *(names[6]);
1814 char_u *tail;
1815 char_u *p;
1816 int num_files;
1817 int file_count = 0;
1818 char_u **files;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001819 char_u *dirp;
1820 char_u *dir_name;
Bram Moolenaar64354da2010-05-25 21:37:17 +02001821 char_u *fname_res = NULL;
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001822#ifdef HAVE_READLINK
1823 char_u fname_buf[MAXPATHL];
Bram Moolenaar64354da2010-05-25 21:37:17 +02001824#endif
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001825
Bram Moolenaar64354da2010-05-25 21:37:17 +02001826 if (fname != NULL)
1827 {
1828#ifdef HAVE_READLINK
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001829 // Expand symlink in the file name, because the swap file is created
1830 // with the actual file instead of with the symlink.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001831 if (resolve_symlink(fname, fname_buf) == OK)
1832 fname_res = fname_buf;
1833 else
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001834#endif
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001835 fname_res = fname;
Bram Moolenaar64354da2010-05-25 21:37:17 +02001836 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001837
Bram Moolenaarc216a7a2022-12-05 13:50:55 +00001838 if (do_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001839 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001840 // use msg() to start the scrolling properly
Bram Moolenaar32526b32019-01-19 17:43:09 +01001841 msg(_("Swap files found:"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001842 msg_putchar('\n');
1843 }
1844
1845 /*
1846 * Do the loop for every directory in 'directory'.
1847 * First allocate some memory to put the directory name in.
1848 */
Bram Moolenaar964b3742019-05-24 18:54:09 +02001849 dir_name = alloc(STRLEN(p_dir) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001850 dirp = p_dir;
1851 while (dir_name != NULL && *dirp)
1852 {
1853 /*
1854 * Isolate a directory name from *dirp and put it in dir_name (we know
1855 * it is large enough, so use 31000 for length).
1856 * Advance dirp to next directory name.
1857 */
1858 (void)copy_option_part(&dirp, dir_name, 31000, ",");
1859
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001860 if (dir_name[0] == '.' && dir_name[1] == NUL) // check current dir
Bram Moolenaar071d4272004-06-13 20:20:40 +00001861 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001862 if (fname == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001863 {
1864#ifdef VMS
1865 names[0] = vim_strsave((char_u *)"*_sw%");
1866#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001867 names[0] = vim_strsave((char_u *)"*.sw?");
Bram Moolenaar071d4272004-06-13 20:20:40 +00001868#endif
Bram Moolenaar4f974752019-02-17 17:44:42 +01001869#if defined(UNIX) || defined(MSWIN)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001870 // For Unix names starting with a dot are special. MS-Windows
1871 // supports this too, on some file systems.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001872 names[1] = vim_strsave((char_u *)".*.sw?");
1873 names[2] = vim_strsave((char_u *)".sw?");
1874 num_names = 3;
1875#else
1876# ifdef VMS
1877 names[1] = vim_strsave((char_u *)".*_sw%");
1878 num_names = 2;
1879# else
1880 num_names = 1;
1881# endif
1882#endif
1883 }
1884 else
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001885 num_names = recov_file_names(names, fname_res, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001886 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001887 else // check directory dir_name
Bram Moolenaar071d4272004-06-13 20:20:40 +00001888 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001889 if (fname == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001890 {
1891#ifdef VMS
1892 names[0] = concat_fnames(dir_name, (char_u *)"*_sw%", TRUE);
1893#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001894 names[0] = concat_fnames(dir_name, (char_u *)"*.sw?", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001895#endif
Bram Moolenaar4f974752019-02-17 17:44:42 +01001896#if defined(UNIX) || defined(MSWIN)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001897 // For Unix names starting with a dot are special. MS-Windows
1898 // supports this too, on some file systems.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001899 names[1] = concat_fnames(dir_name, (char_u *)".*.sw?", TRUE);
1900 names[2] = concat_fnames(dir_name, (char_u *)".sw?", TRUE);
1901 num_names = 3;
1902#else
1903# ifdef VMS
1904 names[1] = concat_fnames(dir_name, (char_u *)".*_sw%", TRUE);
1905 num_names = 2;
1906# else
1907 num_names = 1;
1908# endif
1909#endif
1910 }
1911 else
1912 {
Bram Moolenaar4f974752019-02-17 17:44:42 +01001913#if defined(UNIX) || defined(MSWIN)
Bram Moolenaarb113c3a2017-02-28 21:26:17 +01001914 int len = (int)STRLEN(dir_name);
Bram Moolenaarc525e3a2017-02-18 16:59:02 +01001915
1916 p = dir_name + len;
1917 if (after_pathsep(dir_name, p) && len > 1 && p[-1] == p[-2])
Bram Moolenaar071d4272004-06-13 20:20:40 +00001918 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001919 // Ends with '//', Use Full path for swap name
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001920 tail = make_percent_swname(dir_name, fname_res);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001921 }
1922 else
1923#endif
1924 {
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001925 tail = gettail(fname_res);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001926 tail = concat_fnames(dir_name, tail, TRUE);
1927 }
1928 if (tail == NULL)
1929 num_names = 0;
1930 else
1931 {
1932 num_names = recov_file_names(names, tail, FALSE);
1933 vim_free(tail);
1934 }
1935 }
1936 }
1937
Bram Moolenaar0d3cb732019-05-18 13:05:18 +02001938 // check for out-of-memory
Bram Moolenaarc216a7a2022-12-05 13:50:55 +00001939 for (int i = 0; i < num_names; ++i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001940 {
1941 if (names[i] == NULL)
1942 {
1943 for (i = 0; i < num_names; ++i)
1944 vim_free(names[i]);
1945 num_names = 0;
1946 }
1947 }
1948 if (num_names == 0)
1949 num_files = 0;
1950 else if (expand_wildcards(num_names, names, &num_files, &files,
Bram Moolenaar99499b12019-05-23 21:35:48 +02001951 EW_NOTENV|EW_KEEPALL|EW_FILE|EW_SILENT) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001952 num_files = 0;
1953
1954 /*
1955 * When no swap file found, wildcard expansion might have failed (e.g.
1956 * not able to execute the shell).
1957 * Try finding a swap file by simply adding ".swp" to the file name.
1958 */
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001959 if (*dirp == NUL && file_count + num_files == 0 && fname != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001960 {
Bram Moolenaar8767f522016-07-01 17:17:39 +02001961 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001962 char_u *swapname;
1963
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001964 swapname = modname(fname_res,
Bram Moolenaare60acc12011-05-10 16:41:25 +02001965#if defined(VMS)
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001966 (char_u *)"_swp", FALSE
Bram Moolenaar071d4272004-06-13 20:20:40 +00001967#else
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001968 (char_u *)".swp", TRUE
Bram Moolenaar071d4272004-06-13 20:20:40 +00001969#endif
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001970 );
Bram Moolenaar071d4272004-06-13 20:20:40 +00001971 if (swapname != NULL)
1972 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001973 if (mch_stat((char *)swapname, &st) != -1) // It exists!
Bram Moolenaar071d4272004-06-13 20:20:40 +00001974 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001975 files = ALLOC_ONE(char_u *);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001976 if (files != NULL)
1977 {
1978 files[0] = swapname;
1979 swapname = NULL;
1980 num_files = 1;
1981 }
1982 }
1983 vim_free(swapname);
1984 }
1985 }
1986
1987 /*
Bram Moolenaarc216a7a2022-12-05 13:50:55 +00001988 * Remove swapfile name of the current buffer, it must be ignored.
1989 * But keep it for swapfilelist().
Bram Moolenaar071d4272004-06-13 20:20:40 +00001990 */
1991 if (curbuf->b_ml.ml_mfp != NULL
Bram Moolenaarc216a7a2022-12-05 13:50:55 +00001992 && (p = curbuf->b_ml.ml_mfp->mf_fname) != NULL
1993 && ret_list == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001994 {
Bram Moolenaarc216a7a2022-12-05 13:50:55 +00001995 for (int i = 0; i < num_files; ++i)
Bram Moolenaar99499b12019-05-23 21:35:48 +02001996 // Do not expand wildcards, on windows would try to expand
1997 // "%tmp%" in "%tmp%file".
1998 if (fullpathcmp(p, files[i], TRUE, FALSE) & FPC_SAME)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001999 {
Bram Moolenaar99499b12019-05-23 21:35:48 +02002000 // Remove the name from files[i]. Move further entries
2001 // down. When the array becomes empty free it here, since
2002 // FreeWild() won't be called below.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002003 vim_free(files[i]);
Bram Moolenaar9439cdd2009-04-22 13:39:36 +00002004 if (--num_files == 0)
2005 vim_free(files);
2006 else
2007 for ( ; i < num_files; ++i)
2008 files[i] = files[i + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002009 }
2010 }
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00002011 if (nr > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002012 {
2013 file_count += num_files;
2014 if (nr <= file_count)
2015 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002016 *fname_out = vim_strsave(
2017 files[nr - 1 + num_files - file_count]);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002018 dirp = (char_u *)""; // stop searching
Bram Moolenaar071d4272004-06-13 20:20:40 +00002019 }
2020 }
Bram Moolenaarc216a7a2022-12-05 13:50:55 +00002021 else if (do_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002022 {
2023 if (dir_name[0] == '.' && dir_name[1] == NUL)
2024 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002025 if (fname == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002026 msg_puts(_(" In current directory:\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002027 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002028 msg_puts(_(" Using specified name:\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002029 }
2030 else
2031 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002032 msg_puts(_(" In directory "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002033 msg_home_replace(dir_name);
Bram Moolenaar32526b32019-01-19 17:43:09 +01002034 msg_puts(":\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00002035 }
2036
2037 if (num_files)
2038 {
Bram Moolenaarc216a7a2022-12-05 13:50:55 +00002039 for (int i = 0; i < num_files; ++i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002040 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002041 // print the swap file name
Bram Moolenaar071d4272004-06-13 20:20:40 +00002042 msg_outnum((long)++file_count);
Bram Moolenaar32526b32019-01-19 17:43:09 +01002043 msg_puts(". ");
2044 msg_puts((char *)gettail(files[i]));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002045 msg_putchar('\n');
2046 (void)swapfile_info(files[i]);
2047 }
2048 }
2049 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002050 msg_puts(_(" -- none --\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002051 out_flush();
2052 }
Bram Moolenaarc216a7a2022-12-05 13:50:55 +00002053#ifdef FEAT_EVAL
2054 else if (ret_list != NULL)
2055 {
2056 for (int i = 0; i < num_files; ++i)
2057 {
2058 char_u *name = concat_fnames(dir_name, files[i], TRUE);
2059 if (name != NULL)
2060 {
2061 list_append_string(ret_list, name, -1);
2062 vim_free(name);
2063 }
2064 }
2065 }
2066#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002067 else
2068 file_count += num_files;
2069
Bram Moolenaarc216a7a2022-12-05 13:50:55 +00002070 for (int i = 0; i < num_names; ++i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002071 vim_free(names[i]);
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00002072 if (num_files > 0)
2073 FreeWild(num_files, files);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002074 }
2075 vim_free(dir_name);
2076 return file_count;
2077}
2078
Bram Moolenaar4f974752019-02-17 17:44:42 +01002079#if defined(UNIX) || defined(MSWIN) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002080/*
Bram Moolenaarb782ba42018-08-07 21:39:28 +02002081 * Need _very_ long file names.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002082 * Append the full path to name with path separators made into percent
Bram Moolenaar8b0e62c2021-10-19 22:12:25 +01002083 * signs, to "dir". An unnamed buffer is handled as "" (<currentdir>/"")
2084 * The last character in "dir" must be an extra slash or backslash, it is
2085 * removed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002086 */
Bram Moolenaarb782ba42018-08-07 21:39:28 +02002087 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002088make_percent_swname(char_u *dir, char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002089{
Bram Moolenaarb782ba42018-08-07 21:39:28 +02002090 char_u *d = NULL, *s, *f;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002091
Bram Moolenaarb782ba42018-08-07 21:39:28 +02002092 f = fix_fname(name != NULL ? name : (char_u *)"");
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002093 if (f == NULL)
2094 return NULL;
Bram Moolenaar8b0e62c2021-10-19 22:12:25 +01002095
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002096 s = alloc(STRLEN(f) + 1);
2097 if (s != NULL)
2098 {
2099 STRCPY(s, f);
2100 for (d = s; *d != NUL; MB_PTR_ADV(d))
2101 if (vim_ispathsep(*d))
2102 *d = '%';
2103
2104 dir[STRLEN(dir) - 1] = NUL; // remove one trailing slash
2105 d = concat_fnames(dir, s, TRUE);
2106 vim_free(s);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002107 }
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00002108 vim_free(f);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002109 return d;
2110}
2111#endif
2112
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02002113#if (defined(UNIX) || defined(VMS) || defined(MSWIN)) \
2114 && (defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG))
2115# define HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar071d4272004-06-13 20:20:40 +00002116static int process_still_running;
2117#endif
2118
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002119#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002120/*
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002121 * Return information found in swapfile "fname" in dictionary "d".
2122 * This is used by the swapinfo() function.
2123 */
2124 void
2125get_b0_dict(char_u *fname, dict_T *d)
2126{
2127 int fd;
2128 struct block0 b0;
2129
2130 if ((fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0)) >= 0)
2131 {
2132 if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0))
2133 {
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002134 if (ml_check_b0_id(&b0) == FAIL)
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002135 dict_add_string(d, "error", (char_u *)"Not a swap file");
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002136 else if (b0_magic_wrong(&b0))
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002137 dict_add_string(d, "error", (char_u *)"Magic number mismatch");
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002138 else
2139 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002140 // we have swap information
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002141 dict_add_string_len(d, "version", b0.b0_version, 10);
2142 dict_add_string_len(d, "user", b0.b0_uname, B0_UNAME_SIZE);
2143 dict_add_string_len(d, "host", b0.b0_hname, B0_HNAME_SIZE);
2144 dict_add_string_len(d, "fname", b0.b0_fname, B0_FNAME_SIZE_ORG);
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002145
2146 dict_add_number(d, "pid", char_to_long(b0.b0_pid));
2147 dict_add_number(d, "mtime", char_to_long(b0.b0_mtime));
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002148 dict_add_number(d, "dirty", b0.b0_dirty ? 1 : 0);
2149# ifdef CHECK_INODE
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002150 dict_add_number(d, "inode", char_to_long(b0.b0_ino));
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002151# endif
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002152 }
2153 }
2154 else
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002155 dict_add_string(d, "error", (char_u *)"Cannot read file");
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002156 close(fd);
2157 }
2158 else
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002159 dict_add_string(d, "error", (char_u *)"Cannot open file");
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002160}
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002161#endif
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002162
2163/*
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00002164 * Give information about an existing swap file.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002165 * Returns timestamp (0 when unknown).
2166 */
2167 static time_t
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002168swapfile_info(char_u *fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002169{
Bram Moolenaar8767f522016-07-01 17:17:39 +02002170 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002171 int fd;
2172 struct block0 b0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002173#ifdef UNIX
2174 char_u uname[B0_UNAME_SIZE];
2175#endif
2176
Bram Moolenaar63d25552019-05-10 21:28:38 +02002177 // print the swap file date
Bram Moolenaar071d4272004-06-13 20:20:40 +00002178 if (mch_stat((char *)fname, &st) != -1)
2179 {
2180#ifdef UNIX
Bram Moolenaar63d25552019-05-10 21:28:38 +02002181 // print name of owner of the file
Bram Moolenaar071d4272004-06-13 20:20:40 +00002182 if (mch_get_uname(st.st_uid, uname, B0_UNAME_SIZE) == OK)
2183 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002184 msg_puts(_(" owned by: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002185 msg_outtrans(uname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01002186 msg_puts(_(" dated: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002187 }
2188 else
2189#endif
Bram Moolenaar32526b32019-01-19 17:43:09 +01002190 msg_puts(_(" dated: "));
Bram Moolenaar63d25552019-05-10 21:28:38 +02002191 msg_puts(get_ctime(st.st_mtime, TRUE));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002192 }
Bram Moolenaar63d25552019-05-10 21:28:38 +02002193 else
2194 st.st_mtime = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002195
2196 /*
2197 * print the original file name
2198 */
2199 fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
2200 if (fd >= 0)
2201 {
Bram Moolenaar540fc6f2010-12-17 16:27:16 +01002202 if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002203 {
2204 if (STRNCMP(b0.b0_version, "VIM 3.0", 7) == 0)
2205 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002206 msg_puts(_(" [from Vim version 3.0]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002207 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002208 else if (ml_check_b0_id(&b0) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002209 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002210 msg_puts(_(" [does not look like a Vim swap file]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002211 }
2212 else
2213 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002214 msg_puts(_(" file name: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002215 if (b0.b0_fname[0] == NUL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002216 msg_puts(_("[No Name]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002217 else
2218 msg_outtrans(b0.b0_fname);
2219
Bram Moolenaar32526b32019-01-19 17:43:09 +01002220 msg_puts(_("\n modified: "));
2221 msg_puts(b0.b0_dirty ? _("YES") : _("no"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002222
2223 if (*(b0.b0_uname) != NUL)
2224 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002225 msg_puts(_("\n user name: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002226 msg_outtrans(b0.b0_uname);
2227 }
2228
2229 if (*(b0.b0_hname) != NUL)
2230 {
2231 if (*(b0.b0_uname) != NUL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002232 msg_puts(_(" host name: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002233 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002234 msg_puts(_("\n host name: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002235 msg_outtrans(b0.b0_hname);
2236 }
2237
2238 if (char_to_long(b0.b0_pid) != 0L)
2239 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002240 msg_puts(_("\n process ID: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002241 msg_outnum(char_to_long(b0.b0_pid));
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002242#if defined(UNIX) || defined(MSWIN)
Bram Moolenaarf52f0602021-03-10 21:26:37 +01002243 if (swapfile_process_running(&b0, fname))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002244 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002245 msg_puts(_(" (STILL RUNNING)"));
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02002246# ifdef HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar071d4272004-06-13 20:20:40 +00002247 process_still_running = TRUE;
2248# endif
2249 }
2250#endif
2251 }
2252
2253 if (b0_magic_wrong(&b0))
2254 {
Bram Moolenaar48e330a2016-02-23 14:53:34 +01002255#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002256 if (STRNCMP(b0.b0_hname, "PC ", 3) == 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002257 msg_puts(_("\n [not usable with this version of Vim]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002258 else
2259#endif
Bram Moolenaar32526b32019-01-19 17:43:09 +01002260 msg_puts(_("\n [not usable on this computer]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002261 }
2262 }
2263 }
2264 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002265 msg_puts(_(" [cannot be read]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002266 close(fd);
2267 }
2268 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002269 msg_puts(_(" [cannot be opened]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002270 msg_putchar('\n');
2271
Bram Moolenaar63d25552019-05-10 21:28:38 +02002272 return st.st_mtime;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002273}
2274
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002275/*
2276 * Return TRUE if the swap file looks OK and there are no changes, thus it can
2277 * be safely deleted.
2278 */
zeertzjq3c5999e2022-03-23 13:54:51 +00002279 static int
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002280swapfile_unchanged(char_u *fname)
2281{
2282 stat_T st;
2283 int fd;
2284 struct block0 b0;
2285 int ret = TRUE;
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002286
2287 // must be able to stat the swap file
2288 if (mch_stat((char *)fname, &st) == -1)
2289 return FALSE;
2290
2291 // must be able to read the first block
2292 fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
2293 if (fd < 0)
2294 return FALSE;
2295 if (read_eintr(fd, &b0, sizeof(b0)) != sizeof(b0))
2296 {
2297 close(fd);
2298 return FALSE;
2299 }
2300
2301 // the ID and magic number must be correct
2302 if (ml_check_b0_id(&b0) == FAIL|| b0_magic_wrong(&b0))
2303 ret = FALSE;
2304
2305 // must be unchanged
2306 if (b0.b0_dirty)
2307 ret = FALSE;
2308
2309#if defined(UNIX) || defined(MSWIN)
Bram Moolenaarf8835082020-11-09 21:04:17 +01002310 // Host name must be known and must equal the current host name, otherwise
2311 // comparing pid is meaningless.
2312 if (*(b0.b0_hname) == NUL)
2313 {
2314 ret = FALSE;
2315 }
2316 else
2317 {
2318 char_u hostname[B0_HNAME_SIZE];
2319
2320 mch_get_host_name(hostname, B0_HNAME_SIZE);
2321 hostname[B0_HNAME_SIZE - 1] = NUL;
Bram Moolenaare79cdb62020-11-21 13:51:16 +01002322 b0.b0_hname[B0_HNAME_SIZE - 1] = NUL; // in case of corruption
Bram Moolenaarf8835082020-11-09 21:04:17 +01002323 if (STRICMP(b0.b0_hname, hostname) != 0)
2324 ret = FALSE;
2325 }
2326
Bram Moolenaar32aa1022019-11-02 22:54:41 +01002327 // process must be known and not be running
Bram Moolenaarf52f0602021-03-10 21:26:37 +01002328 if (char_to_long(b0.b0_pid) == 0L || swapfile_process_running(&b0, fname))
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002329 ret = FALSE;
2330#endif
2331
Bram Moolenaarf8835082020-11-09 21:04:17 +01002332 // We do not check the user, it should be irrelevant for whether the swap
2333 // file is still useful.
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002334
2335 close(fd);
2336 return ret;
2337}
2338
Bram Moolenaar071d4272004-06-13 20:20:40 +00002339 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002340recov_file_names(char_u **names, char_u *path, int prepend_dot)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002341{
2342 int num_names;
2343
Bram Moolenaar071d4272004-06-13 20:20:40 +00002344 /*
2345 * (Win32 and Win64) never short names, but do prepend a dot.
2346 * (Not MS-DOS or Win32 or Win64) maybe short name, maybe not: Try both.
2347 * Only use the short name if it is different.
2348 */
2349 char_u *p;
2350 int i;
Bram Moolenaar4f974752019-02-17 17:44:42 +01002351# ifndef MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002352 int shortname = curbuf->b_shortname;
2353
2354 curbuf->b_shortname = FALSE;
2355# endif
2356
2357 num_names = 0;
2358
2359 /*
2360 * May also add the file name with a dot prepended, for swap file in same
2361 * dir as original file.
2362 */
2363 if (prepend_dot)
2364 {
2365 names[num_names] = modname(path, (char_u *)".sw?", TRUE);
2366 if (names[num_names] == NULL)
2367 goto end;
2368 ++num_names;
2369 }
2370
2371 /*
2372 * Form the normal swap file name pattern by appending ".sw?".
2373 */
2374#ifdef VMS
2375 names[num_names] = concat_fnames(path, (char_u *)"_sw%", FALSE);
2376#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002377 names[num_names] = concat_fnames(path, (char_u *)".sw?", FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002378#endif
2379 if (names[num_names] == NULL)
2380 goto end;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002381 if (num_names >= 1) // check if we have the same name twice
Bram Moolenaar071d4272004-06-13 20:20:40 +00002382 {
2383 p = names[num_names - 1];
2384 i = (int)STRLEN(names[num_names - 1]) - (int)STRLEN(names[num_names]);
2385 if (i > 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002386 p += i; // file name has been expanded to full path
Bram Moolenaar071d4272004-06-13 20:20:40 +00002387
2388 if (STRCMP(p, names[num_names]) != 0)
2389 ++num_names;
2390 else
2391 vim_free(names[num_names]);
2392 }
2393 else
2394 ++num_names;
2395
Bram Moolenaar4f974752019-02-17 17:44:42 +01002396# ifndef MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002397 /*
2398 * Also try with 'shortname' set, in case the file is on a DOS filesystem.
2399 */
2400 curbuf->b_shortname = TRUE;
2401#ifdef VMS
2402 names[num_names] = modname(path, (char_u *)"_sw%", FALSE);
2403#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002404 names[num_names] = modname(path, (char_u *)".sw?", FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002405#endif
2406 if (names[num_names] == NULL)
2407 goto end;
2408
2409 /*
2410 * Remove the one from 'shortname', if it's the same as with 'noshortname'.
2411 */
2412 p = names[num_names];
2413 i = STRLEN(names[num_names]) - STRLEN(names[num_names - 1]);
2414 if (i > 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002415 p += i; // file name has been expanded to full path
Bram Moolenaar071d4272004-06-13 20:20:40 +00002416 if (STRCMP(names[num_names - 1], p) == 0)
2417 vim_free(names[num_names]);
2418 else
2419 ++num_names;
2420# endif
2421
2422end:
Bram Moolenaar4f974752019-02-17 17:44:42 +01002423# ifndef MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002424 curbuf->b_shortname = shortname;
2425# endif
2426
Bram Moolenaar071d4272004-06-13 20:20:40 +00002427 return num_names;
2428}
2429
2430/*
2431 * sync all memlines
2432 *
2433 * If 'check_file' is TRUE, check if original file exists and was not changed.
2434 * If 'check_char' is TRUE, stop syncing when character becomes available, but
2435 * always sync at least one block.
2436 */
2437 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002438ml_sync_all(int check_file, int check_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002439{
2440 buf_T *buf;
Bram Moolenaar8767f522016-07-01 17:17:39 +02002441 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002442
Bram Moolenaar29323592016-07-24 22:04:11 +02002443 FOR_ALL_BUFFERS(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002444 {
Christian Brabandtf573c6e2021-06-20 14:02:16 +02002445 if (buf->b_ml.ml_mfp == NULL
2446 || buf->b_ml.ml_mfp->mf_fname == NULL
2447 || buf->b_ml.ml_mfp->mf_fd < 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002448 continue; // no file
Bram Moolenaar071d4272004-06-13 20:20:40 +00002449
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002450 ml_flush_line(buf); // flush buffered line
2451 // flush locked block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002452 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH);
2453 if (bufIsChanged(buf) && check_file && mf_need_trans(buf->b_ml.ml_mfp)
2454 && buf->b_ffname != NULL)
2455 {
2456 /*
2457 * If the original file does not exist anymore or has been changed
2458 * call ml_preserve() to get rid of all negative numbered blocks.
2459 */
2460 if (mch_stat((char *)buf->b_ffname, &st) == -1
2461 || st.st_mtime != buf->b_mtime_read
Leah Neukirchen0a7984a2021-10-14 21:27:55 +01002462#ifdef ST_MTIM_NSEC
2463 || st.ST_MTIM_NSEC != buf->b_mtime_read_ns
2464#endif
Bram Moolenaar914703b2010-05-31 21:59:46 +02002465 || st.st_size != buf->b_orig_size)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002466 {
2467 ml_preserve(buf, FALSE);
2468 did_check_timestamps = FALSE;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002469 need_check_timestamps = TRUE; // give message later
Bram Moolenaar071d4272004-06-13 20:20:40 +00002470 }
2471 }
2472 if (buf->b_ml.ml_mfp->mf_dirty)
2473 {
2474 (void)mf_sync(buf->b_ml.ml_mfp, (check_char ? MFS_STOP : 0)
2475 | (bufIsChanged(buf) ? MFS_FLUSH : 0));
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002476 if (check_char && ui_char_avail()) // character available now
Bram Moolenaar071d4272004-06-13 20:20:40 +00002477 break;
2478 }
2479 }
2480}
2481
2482/*
2483 * sync one buffer, including negative blocks
2484 *
2485 * after this all the blocks are in the swap file
2486 *
2487 * Used for the :preserve command and when the original file has been
2488 * changed or deleted.
2489 *
2490 * when message is TRUE the success of preserving is reported
2491 */
2492 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002493ml_preserve(buf_T *buf, int message)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002494{
2495 bhdr_T *hp;
2496 linenr_T lnum;
2497 memfile_T *mfp = buf->b_ml.ml_mfp;
2498 int status;
2499 int got_int_save = got_int;
2500
2501 if (mfp == NULL || mfp->mf_fname == NULL)
2502 {
2503 if (message)
Bram Moolenaareaaac012022-01-02 17:00:40 +00002504 emsg(_(e_cannot_preserve_there_is_no_swap_file));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002505 return;
2506 }
2507
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002508 // We only want to stop when interrupted here, not when interrupted
2509 // before.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002510 got_int = FALSE;
2511
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002512 ml_flush_line(buf); // flush buffered line
2513 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush locked block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002514 status = mf_sync(mfp, MFS_ALL | MFS_FLUSH);
2515
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002516 // stack is invalid after mf_sync(.., MFS_ALL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002517 buf->b_ml.ml_stack_top = 0;
2518
2519 /*
2520 * Some of the data blocks may have been changed from negative to
2521 * positive block number. In that case the pointer blocks need to be
2522 * updated.
2523 *
2524 * We don't know in which pointer block the references are, so we visit
2525 * all data blocks until there are no more translations to be done (or
2526 * we hit the end of the file, which can only happen in case a write fails,
2527 * e.g. when file system if full).
2528 * ml_find_line() does the work by translating the negative block numbers
2529 * when getting the first line of each data block.
2530 */
2531 if (mf_need_trans(mfp) && !got_int)
2532 {
2533 lnum = 1;
2534 while (mf_need_trans(mfp) && lnum <= buf->b_ml.ml_line_count)
2535 {
2536 hp = ml_find_line(buf, lnum, ML_FIND);
2537 if (hp == NULL)
2538 {
2539 status = FAIL;
2540 goto theend;
2541 }
2542 CHECK(buf->b_ml.ml_locked_low != lnum, "low != lnum");
2543 lnum = buf->b_ml.ml_locked_high + 1;
2544 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002545 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush locked block
2546 // sync the updated pointer blocks
Bram Moolenaar071d4272004-06-13 20:20:40 +00002547 if (mf_sync(mfp, MFS_ALL | MFS_FLUSH) == FAIL)
2548 status = FAIL;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002549 buf->b_ml.ml_stack_top = 0; // stack is invalid now
Bram Moolenaar071d4272004-06-13 20:20:40 +00002550 }
2551theend:
2552 got_int |= got_int_save;
2553
2554 if (message)
2555 {
2556 if (status == OK)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002557 msg(_("File preserved"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002558 else
Bram Moolenaareaaac012022-01-02 17:00:40 +00002559 emsg(_(e_preserve_failed));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002560 }
2561}
2562
2563/*
2564 * NOTE: The pointer returned by the ml_get_*() functions only remains valid
2565 * until the next call!
2566 * line1 = ml_get(1);
2567 * line2 = ml_get(2); // line1 is now invalid!
2568 * Make a copy of the line if necessary.
2569 */
2570/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002571 * Return a pointer to a (read-only copy of a) line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002572 *
2573 * On failure an error message is given and IObuff is returned (to avoid
2574 * having to check for error everywhere).
2575 */
2576 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002577ml_get(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002578{
2579 return ml_get_buf(curbuf, lnum, FALSE);
2580}
2581
2582/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002583 * Return pointer to position "pos".
Bram Moolenaar071d4272004-06-13 20:20:40 +00002584 */
2585 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002586ml_get_pos(pos_T *pos)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002587{
2588 return (ml_get_buf(curbuf, pos->lnum, FALSE) + pos->col);
2589}
2590
2591/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002592 * Return pointer to cursor line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002593 */
2594 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002595ml_get_curline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002596{
2597 return ml_get_buf(curbuf, curwin->w_cursor.lnum, FALSE);
2598}
2599
2600/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002601 * Return pointer to cursor position.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002602 */
2603 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002604ml_get_cursor(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002605{
2606 return (ml_get_buf(curbuf, curwin->w_cursor.lnum, FALSE) +
2607 curwin->w_cursor.col);
2608}
2609
2610/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002611 * Return a pointer to a line in a specific buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +00002612 *
2613 * "will_change": if TRUE mark the buffer dirty (chars in the line will be
2614 * changed)
2615 */
2616 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002617ml_get_buf(
2618 buf_T *buf,
2619 linenr_T lnum,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002620 int will_change) // line will be changed
Bram Moolenaar071d4272004-06-13 20:20:40 +00002621{
Bram Moolenaarad40f022007-02-13 03:01:39 +00002622 bhdr_T *hp;
2623 DATA_BL *dp;
Bram Moolenaarad40f022007-02-13 03:01:39 +00002624 static int recursive = 0;
Bram Moolenaar96e7a592021-11-25 13:52:37 +00002625 static char_u questions[4];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002626
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002627 if (lnum > buf->b_ml.ml_line_count) // invalid line number
Bram Moolenaar071d4272004-06-13 20:20:40 +00002628 {
Bram Moolenaarad40f022007-02-13 03:01:39 +00002629 if (recursive == 0)
2630 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002631 // Avoid giving this message for a recursive call, may happen when
2632 // the GUI redraws part of the text.
Bram Moolenaarad40f022007-02-13 03:01:39 +00002633 ++recursive;
Bram Moolenaareaaac012022-01-02 17:00:40 +00002634 siemsg(_(e_ml_get_invalid_lnum_nr), lnum);
Bram Moolenaarad40f022007-02-13 03:01:39 +00002635 --recursive;
2636 }
Bram Moolenaarf9435e42022-02-16 16:33:28 +00002637 ml_flush_line(buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002638errorret:
Bram Moolenaar96e7a592021-11-25 13:52:37 +00002639 STRCPY(questions, "???");
Bram Moolenaaradfde112019-05-25 22:11:45 +02002640 buf->b_ml.ml_line_len = 4;
Bram Moolenaarf9435e42022-02-16 16:33:28 +00002641 buf->b_ml.ml_line_lnum = lnum;
Bram Moolenaar96e7a592021-11-25 13:52:37 +00002642 return questions;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002643 }
Bram Moolenaaradfde112019-05-25 22:11:45 +02002644 if (lnum <= 0) // pretend line 0 is line 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00002645 lnum = 1;
2646
Bram Moolenaaradfde112019-05-25 22:11:45 +02002647 if (buf->b_ml.ml_mfp == NULL) // there are no lines
2648 {
2649 buf->b_ml.ml_line_len = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002650 return (char_u *)"";
Bram Moolenaaradfde112019-05-25 22:11:45 +02002651 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002652
Bram Moolenaar37d619f2010-03-10 14:46:26 +01002653 /*
2654 * See if it is the same line as requested last time.
2655 * Otherwise may need to flush last used line.
2656 * Don't use the last used line when 'swapfile' is reset, need to load all
2657 * blocks.
2658 */
Bram Moolenaar47b8b152007-02-07 02:41:57 +00002659 if (buf->b_ml.ml_line_lnum != lnum || mf_dont_release)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002660 {
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002661 unsigned start, end;
2662 colnr_T len;
2663 int idx;
2664
Bram Moolenaar071d4272004-06-13 20:20:40 +00002665 ml_flush_line(buf);
2666
2667 /*
2668 * Find the data block containing the line.
2669 * This also fills the stack with the blocks from the root to the data
2670 * block and releases any locked block.
2671 */
2672 if ((hp = ml_find_line(buf, lnum, ML_FIND)) == NULL)
2673 {
Bram Moolenaarad40f022007-02-13 03:01:39 +00002674 if (recursive == 0)
2675 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002676 // Avoid giving this message for a recursive call, may happen
2677 // when the GUI redraws part of the text.
Bram Moolenaarad40f022007-02-13 03:01:39 +00002678 ++recursive;
Bram Moolenaarcb868932019-10-26 20:56:21 +02002679 get_trans_bufname(buf);
2680 shorten_dir(NameBuff);
Bram Moolenaareaaac012022-01-02 17:00:40 +00002681 siemsg(_(e_ml_get_cannot_find_line_nr_in_buffer_nr_str),
Bram Moolenaarcb868932019-10-26 20:56:21 +02002682 lnum, buf->b_fnum, NameBuff);
Bram Moolenaarad40f022007-02-13 03:01:39 +00002683 --recursive;
2684 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002685 goto errorret;
2686 }
2687
2688 dp = (DATA_BL *)(hp->bh_data);
2689
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002690 idx = lnum - buf->b_ml.ml_locked_low;
2691 start = ((dp->db_index[idx]) & DB_INDEX_MASK);
2692 // The text ends where the previous line starts. The first line ends
2693 // at the end of the block.
2694 if (idx == 0)
2695 end = dp->db_txt_end;
2696 else
2697 end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK);
2698 len = end - start;
2699
2700 buf->b_ml.ml_line_ptr = (char_u *)dp + start;
2701 buf->b_ml.ml_line_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002702 buf->b_ml.ml_line_lnum = lnum;
Bram Moolenaarfa4873c2022-06-30 22:13:59 +01002703 buf->b_ml.ml_flags &= ~(ML_LINE_DIRTY | ML_ALLOCATED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002704 }
2705 if (will_change)
Bram Moolenaarfa4873c2022-06-30 22:13:59 +01002706 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002707 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
Bram Moolenaarfa4873c2022-06-30 22:13:59 +01002708#ifdef FEAT_EVAL
2709 if (ml_get_alloc_lines && (buf->b_ml.ml_flags & ML_ALLOCATED))
2710 // can't make the change in the data block
2711 buf->b_ml.ml_flags |= ML_LINE_DIRTY;
2712#endif
2713 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002714
Bram Moolenaarfa4873c2022-06-30 22:13:59 +01002715#ifdef FEAT_EVAL
2716 if (ml_get_alloc_lines
2717 && (buf->b_ml.ml_flags & (ML_LINE_DIRTY | ML_ALLOCATED)) == 0)
2718 {
2719 char_u *p = alloc(buf->b_ml.ml_line_len);
2720
2721 // make sure the text is in allocated memory
2722 if (p != NULL)
2723 {
2724 memmove(p, buf->b_ml.ml_line_ptr, buf->b_ml.ml_line_len);
2725 buf->b_ml.ml_line_ptr = p;
2726 buf->b_ml.ml_flags |= ML_ALLOCATED;
2727 if (will_change)
2728 // can't make the change in the data block
2729 buf->b_ml.ml_flags |= ML_LINE_DIRTY;
2730 }
2731 }
2732#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002733 return buf->b_ml.ml_line_ptr;
2734}
2735
2736/*
2737 * Check if a line that was just obtained by a call to ml_get
2738 * is in allocated memory.
Bram Moolenaarfa4873c2022-06-30 22:13:59 +01002739 * This ignores ML_ALLOCATED to get the same behavior as without the test
2740 * override.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002741 */
2742 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002743ml_line_alloced(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002744{
2745 return (curbuf->b_ml.ml_flags & ML_LINE_DIRTY);
2746}
2747
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002748#ifdef FEAT_PROP_POPUP
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002749/*
2750 * Add text properties that continue from the previous line.
2751 */
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002752 static void
2753add_text_props_for_append(
2754 buf_T *buf,
2755 linenr_T lnum,
2756 char_u **line,
2757 int *len,
2758 char_u **tofree)
2759{
2760 int round;
2761 int new_prop_count = 0;
2762 int count;
2763 int n;
2764 char_u *props;
Bram Moolenaarea781452019-09-04 18:53:12 +02002765 int new_len = 0; // init for gcc
Bram Moolenaar8f13d822020-09-12 21:04:23 +02002766 char_u *new_line = NULL;
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002767 textprop_T prop;
2768
2769 // Make two rounds:
2770 // 1. calculate the extra space needed
2771 // 2. allocate the space and fill it
2772 for (round = 1; round <= 2; ++round)
2773 {
2774 if (round == 2)
2775 {
2776 if (new_prop_count == 0)
2777 return; // nothing to do
2778 new_len = *len + new_prop_count * sizeof(textprop_T);
Bram Moolenaar964b3742019-05-24 18:54:09 +02002779 new_line = alloc(new_len);
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002780 if (new_line == NULL)
2781 return;
2782 mch_memmove(new_line, *line, *len);
2783 new_prop_count = 0;
2784 }
2785
2786 // Get the line above to find any props that continue in the next
2787 // line.
2788 count = get_text_props(buf, lnum, &props, FALSE);
2789 for (n = 0; n < count; ++n)
2790 {
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002791 mch_memmove(&prop, props + n * sizeof(textprop_T),
2792 sizeof(textprop_T));
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002793 if (prop.tp_flags & TP_FLAG_CONT_NEXT)
2794 {
2795 if (round == 2)
2796 {
2797 prop.tp_flags |= TP_FLAG_CONT_PREV;
2798 prop.tp_col = 1;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002799 prop.tp_len = *len; // not exactly the right length
2800 mch_memmove(new_line + *len + new_prop_count
2801 * sizeof(textprop_T), &prop, sizeof(textprop_T));
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002802 }
2803 ++new_prop_count;
2804 }
2805 }
2806 }
2807 *line = new_line;
2808 *tofree = new_line;
2809 *len = new_len;
2810}
2811#endif
2812
Bram Moolenaar071d4272004-06-13 20:20:40 +00002813 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002814ml_append_int(
2815 buf_T *buf,
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002816 linenr_T lnum, // append after this line (can be 0)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002817 char_u *line_arg, // text of the new line
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002818 colnr_T len_arg, // length of line, including NUL, or 0
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002819 int flags) // ML_APPEND_ flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00002820{
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002821 char_u *line = line_arg;
2822 colnr_T len = len_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002823 int i;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002824 int line_count; // number of indexes in current block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002825 int offset;
2826 int from, to;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002827 int space_needed; // space needed for new line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002828 int page_size;
2829 int page_count;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002830 int db_idx; // index for lnum in data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002831 bhdr_T *hp;
2832 memfile_T *mfp;
2833 DATA_BL *dp;
2834 PTR_BL *pp;
2835 infoptr_T *ip;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002836#ifdef FEAT_PROP_POPUP
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002837 char_u *tofree = NULL;
Bram Moolenaar50652b02022-08-07 21:48:37 +01002838# ifdef FEAT_BYTEOFF
2839 colnr_T text_len = 0; // text len with NUL without text properties
2840# endif
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002841#endif
2842 int ret = FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002843
Bram Moolenaar071d4272004-06-13 20:20:40 +00002844 if (lnum > buf->b_ml.ml_line_count || buf->b_ml.ml_mfp == NULL)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002845 return FAIL; // lnum out of range
Bram Moolenaar071d4272004-06-13 20:20:40 +00002846
2847 if (lowest_marked && lowest_marked > lnum)
2848 lowest_marked = lnum + 1;
2849
2850 if (len == 0)
Bram Moolenaar50652b02022-08-07 21:48:37 +01002851 {
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002852 len = (colnr_T)STRLEN(line) + 1; // space needed for the text
Bram Moolenaar50652b02022-08-07 21:48:37 +01002853#if defined(FEAT_PROP_POPUP) && defined(FEAT_BYTEOFF)
2854 text_len = len;
2855#endif
2856 }
2857#if defined(FEAT_PROP_POPUP) && defined(FEAT_BYTEOFF)
2858 else if (curbuf->b_has_textprop)
2859 // "len" may include text properties, get the length of the text.
2860 text_len = (colnr_T)STRLEN(line) + 1;
2861 else
2862 text_len = len;
2863#endif
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002864
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002865#ifdef FEAT_PROP_POPUP
Bram Moolenaar840f91f2021-05-26 22:32:10 +02002866 if (curbuf->b_has_textprop && lnum > 0
2867 && !(flags & (ML_APPEND_UNDO | ML_APPEND_NOPROP)))
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002868 // Add text properties that continue from the previous line.
2869 add_text_props_for_append(buf, lnum, &line, &len, &tofree);
2870#endif
2871
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002872 space_needed = len + INDEX_SIZE; // space needed for text + index
Bram Moolenaar071d4272004-06-13 20:20:40 +00002873
2874 mfp = buf->b_ml.ml_mfp;
2875 page_size = mfp->mf_page_size;
2876
2877/*
2878 * find the data block containing the previous line
2879 * This also fills the stack with the blocks from the root to the data block
2880 * This also releases any locked block.
2881 */
2882 if ((hp = ml_find_line(buf, lnum == 0 ? (linenr_T)1 : lnum,
2883 ML_INSERT)) == NULL)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002884 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002885
2886 buf->b_ml.ml_flags &= ~ML_EMPTY;
2887
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002888 if (lnum == 0) // got line one instead, correct db_idx
2889 db_idx = -1; // careful, it is negative!
Bram Moolenaar071d4272004-06-13 20:20:40 +00002890 else
2891 db_idx = lnum - buf->b_ml.ml_locked_low;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002892 // get line count before the insertion
Bram Moolenaar071d4272004-06-13 20:20:40 +00002893 line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low;
2894
2895 dp = (DATA_BL *)(hp->bh_data);
2896
2897/*
2898 * If
2899 * - there is not enough room in the current block
2900 * - appending to the last line in the block
2901 * - not appending to the last line in the file
2902 * insert in front of the next block.
2903 */
2904 if ((int)dp->db_free < space_needed && db_idx == line_count - 1
2905 && lnum < buf->b_ml.ml_line_count)
2906 {
2907 /*
2908 * Now that the line is not going to be inserted in the block that we
2909 * expected, the line count has to be adjusted in the pointer blocks
2910 * by using ml_locked_lineadd.
2911 */
2912 --(buf->b_ml.ml_locked_lineadd);
2913 --(buf->b_ml.ml_locked_high);
2914 if ((hp = ml_find_line(buf, lnum + 1, ML_INSERT)) == NULL)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002915 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002916
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002917 db_idx = -1; // careful, it is negative!
2918 // get line count before the insertion
Bram Moolenaar071d4272004-06-13 20:20:40 +00002919 line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low;
2920 CHECK(buf->b_ml.ml_locked_low != lnum + 1, "locked_low != lnum + 1");
2921
2922 dp = (DATA_BL *)(hp->bh_data);
2923 }
2924
2925 ++buf->b_ml.ml_line_count;
2926
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002927 if ((int)dp->db_free >= space_needed) // enough room in data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002928 {
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002929 /*
2930 * Insert the new line in an existing data block, or in the data block
2931 * allocated above.
2932 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002933 dp->db_txt_start -= len;
2934 dp->db_free -= space_needed;
2935 ++(dp->db_line_count);
2936
2937 /*
2938 * move the text of the lines that follow to the front
2939 * adjust the indexes of the lines that follow
2940 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002941 if (line_count > db_idx + 1) // if there are following lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00002942 {
2943 /*
2944 * Offset is the start of the previous line.
2945 * This will become the character just after the new line.
2946 */
2947 if (db_idx < 0)
2948 offset = dp->db_txt_end;
2949 else
2950 offset = ((dp->db_index[db_idx]) & DB_INDEX_MASK);
2951 mch_memmove((char *)dp + dp->db_txt_start,
2952 (char *)dp + dp->db_txt_start + len,
2953 (size_t)(offset - (dp->db_txt_start + len)));
2954 for (i = line_count - 1; i > db_idx; --i)
2955 dp->db_index[i + 1] = dp->db_index[i] - len;
2956 dp->db_index[db_idx + 1] = offset - len;
2957 }
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002958 else
2959 // add line at the end (which is the start of the text)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002960 dp->db_index[db_idx + 1] = dp->db_txt_start;
2961
2962 /*
2963 * copy the text into the block
2964 */
2965 mch_memmove((char *)dp + dp->db_index[db_idx + 1], line, (size_t)len);
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002966 if (flags & ML_APPEND_MARK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002967 dp->db_index[db_idx + 1] |= DB_MARKED;
2968
2969 /*
2970 * Mark the block dirty.
2971 */
2972 buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002973 if (!(flags & ML_APPEND_NEW))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002974 buf->b_ml.ml_flags |= ML_LOCKED_POS;
2975 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002976 else // not enough space in data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002977 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002978 long line_count_left, line_count_right;
2979 int page_count_left, page_count_right;
2980 bhdr_T *hp_left;
2981 bhdr_T *hp_right;
2982 bhdr_T *hp_new;
2983 int lines_moved;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002984 int data_moved = 0; // init to shut up gcc
2985 int total_moved = 0; // init to shut up gcc
Bram Moolenaar071d4272004-06-13 20:20:40 +00002986 DATA_BL *dp_right, *dp_left;
2987 int stack_idx;
2988 int in_left;
2989 int lineadd;
2990 blocknr_T bnum_left, bnum_right;
2991 linenr_T lnum_left, lnum_right;
2992 int pb_idx;
2993 PTR_BL *pp_new;
2994
2995 /*
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002996 * There is not enough room, we have to create a new data block and
2997 * copy some lines into it.
2998 * Then we have to insert an entry in the pointer block.
2999 * If this pointer block also is full, we go up another block, and so
3000 * on, up to the root if necessary.
3001 * The line counts in the pointer blocks have already been adjusted by
3002 * ml_find_line().
3003 *
Bram Moolenaar071d4272004-06-13 20:20:40 +00003004 * We are going to allocate a new data block. Depending on the
3005 * situation it will be put to the left or right of the existing
3006 * block. If possible we put the new line in the left block and move
3007 * the lines after it to the right block. Otherwise the new line is
3008 * also put in the right block. This method is more efficient when
3009 * inserting a lot of lines at one place.
3010 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003011 if (db_idx < 0) // left block is new, right block is existing
Bram Moolenaar071d4272004-06-13 20:20:40 +00003012 {
3013 lines_moved = 0;
3014 in_left = TRUE;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003015 // space_needed does not change
Bram Moolenaar071d4272004-06-13 20:20:40 +00003016 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003017 else // left block is existing, right block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00003018 {
3019 lines_moved = line_count - db_idx - 1;
3020 if (lines_moved == 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003021 in_left = FALSE; // put new line in right block
3022 // space_needed does not change
Bram Moolenaar071d4272004-06-13 20:20:40 +00003023 else
3024 {
3025 data_moved = ((dp->db_index[db_idx]) & DB_INDEX_MASK) -
3026 dp->db_txt_start;
3027 total_moved = data_moved + lines_moved * INDEX_SIZE;
3028 if ((int)dp->db_free + total_moved >= space_needed)
3029 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003030 in_left = TRUE; // put new line in left block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003031 space_needed = total_moved;
3032 }
3033 else
3034 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003035 in_left = FALSE; // put new line in right block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003036 space_needed += total_moved;
3037 }
3038 }
3039 }
3040
3041 page_count = ((space_needed + HEADER_SIZE) + page_size - 1) / page_size;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003042 if ((hp_new = ml_new_data(mfp, flags & ML_APPEND_NEW, page_count))
3043 == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003044 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003045 // correct line counts in pointer blocks
Bram Moolenaar071d4272004-06-13 20:20:40 +00003046 --(buf->b_ml.ml_locked_lineadd);
3047 --(buf->b_ml.ml_locked_high);
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003048 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003049 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003050 if (db_idx < 0) // left block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00003051 {
3052 hp_left = hp_new;
3053 hp_right = hp;
3054 line_count_left = 0;
3055 line_count_right = line_count;
3056 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003057 else // right block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00003058 {
3059 hp_left = hp;
3060 hp_right = hp_new;
3061 line_count_left = line_count;
3062 line_count_right = 0;
3063 }
3064 dp_right = (DATA_BL *)(hp_right->bh_data);
3065 dp_left = (DATA_BL *)(hp_left->bh_data);
3066 bnum_left = hp_left->bh_bnum;
3067 bnum_right = hp_right->bh_bnum;
3068 page_count_left = hp_left->bh_page_count;
3069 page_count_right = hp_right->bh_page_count;
3070
3071 /*
3072 * May move the new line into the right/new block.
3073 */
3074 if (!in_left)
3075 {
3076 dp_right->db_txt_start -= len;
3077 dp_right->db_free -= len + INDEX_SIZE;
3078 dp_right->db_index[0] = dp_right->db_txt_start;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003079 if (flags & ML_APPEND_MARK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003080 dp_right->db_index[0] |= DB_MARKED;
3081
3082 mch_memmove((char *)dp_right + dp_right->db_txt_start,
3083 line, (size_t)len);
3084 ++line_count_right;
3085 }
3086 /*
3087 * may move lines from the left/old block to the right/new one.
3088 */
3089 if (lines_moved)
3090 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003091 dp_right->db_txt_start -= data_moved;
3092 dp_right->db_free -= total_moved;
3093 mch_memmove((char *)dp_right + dp_right->db_txt_start,
3094 (char *)dp_left + dp_left->db_txt_start,
3095 (size_t)data_moved);
3096 offset = dp_right->db_txt_start - dp_left->db_txt_start;
3097 dp_left->db_txt_start += data_moved;
3098 dp_left->db_free += total_moved;
3099
3100 /*
3101 * update indexes in the new block
3102 */
3103 for (to = line_count_right, from = db_idx + 1;
3104 from < line_count_left; ++from, ++to)
3105 dp_right->db_index[to] = dp->db_index[from] + offset;
3106 line_count_right += lines_moved;
3107 line_count_left -= lines_moved;
3108 }
3109
3110 /*
3111 * May move the new line into the left (old or new) block.
3112 */
3113 if (in_left)
3114 {
3115 dp_left->db_txt_start -= len;
3116 dp_left->db_free -= len + INDEX_SIZE;
3117 dp_left->db_index[line_count_left] = dp_left->db_txt_start;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003118 if (flags & ML_APPEND_MARK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003119 dp_left->db_index[line_count_left] |= DB_MARKED;
3120 mch_memmove((char *)dp_left + dp_left->db_txt_start,
3121 line, (size_t)len);
3122 ++line_count_left;
3123 }
3124
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003125 if (db_idx < 0) // left block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00003126 {
3127 lnum_left = lnum + 1;
3128 lnum_right = 0;
3129 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003130 else // right block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00003131 {
3132 lnum_left = 0;
3133 if (in_left)
3134 lnum_right = lnum + 2;
3135 else
3136 lnum_right = lnum + 1;
3137 }
3138 dp_left->db_line_count = line_count_left;
3139 dp_right->db_line_count = line_count_right;
3140
3141 /*
3142 * release the two data blocks
3143 * The new one (hp_new) already has a correct blocknumber.
3144 * The old one (hp, in ml_locked) gets a positive blocknumber if
3145 * we changed it and we are not editing a new file.
3146 */
3147 if (lines_moved || in_left)
3148 buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003149 if (!(flags & ML_APPEND_NEW) && db_idx >= 0 && in_left)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003150 buf->b_ml.ml_flags |= ML_LOCKED_POS;
3151 mf_put(mfp, hp_new, TRUE, FALSE);
3152
3153 /*
3154 * flush the old data block
3155 * set ml_locked_lineadd to 0, because the updating of the
3156 * pointer blocks is done below
3157 */
3158 lineadd = buf->b_ml.ml_locked_lineadd;
3159 buf->b_ml.ml_locked_lineadd = 0;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003160 ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003161
3162 /*
3163 * update pointer blocks for the new data block
3164 */
3165 for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0;
3166 --stack_idx)
3167 {
3168 ip = &(buf->b_ml.ml_stack[stack_idx]);
3169 pb_idx = ip->ip_index;
3170 if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003171 goto theend;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003172 pp = (PTR_BL *)(hp->bh_data); // must be pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003173 if (pp->pb_id != PTR_ID)
3174 {
Bram Moolenaareaaac012022-01-02 17:00:40 +00003175 iemsg(_(e_pointer_block_id_wrong_three));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003176 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003177 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003178 }
3179 /*
3180 * TODO: If the pointer block is full and we are adding at the end
3181 * try to insert in front of the next block
3182 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003183 // block not full, add one entry
Bram Moolenaar071d4272004-06-13 20:20:40 +00003184 if (pp->pb_count < pp->pb_count_max)
3185 {
3186 if (pb_idx + 1 < (int)pp->pb_count)
3187 mch_memmove(&pp->pb_pointer[pb_idx + 2],
3188 &pp->pb_pointer[pb_idx + 1],
3189 (size_t)(pp->pb_count - pb_idx - 1) * sizeof(PTR_EN));
3190 ++pp->pb_count;
3191 pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
3192 pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
3193 pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
3194 pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right;
3195 pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right;
3196 pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right;
3197
3198 if (lnum_left != 0)
3199 pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left;
3200 if (lnum_right != 0)
3201 pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right;
3202
3203 mf_put(mfp, hp, TRUE, FALSE);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003204 buf->b_ml.ml_stack_top = stack_idx + 1; // truncate stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00003205
3206 if (lineadd)
3207 {
3208 --(buf->b_ml.ml_stack_top);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003209 // fix line count for rest of blocks in the stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00003210 ml_lineadd(buf, lineadd);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003211 // fix stack itself
Bram Moolenaar071d4272004-06-13 20:20:40 +00003212 buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high +=
3213 lineadd;
3214 ++(buf->b_ml.ml_stack_top);
3215 }
3216
3217 /*
3218 * We are finished, break the loop here.
3219 */
3220 break;
3221 }
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01003222 // pointer block full
3223 /*
3224 * split the pointer block
3225 * allocate a new pointer block
3226 * move some of the pointer into the new block
3227 * prepare for updating the parent block
3228 */
3229 for (;;) // do this twice when splitting block 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00003230 {
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01003231 hp_new = ml_new_ptr(mfp);
3232 if (hp_new == NULL) // TODO: try to fix tree
3233 goto theend;
3234 pp_new = (PTR_BL *)(hp_new->bh_data);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003235
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01003236 if (hp->bh_bnum != 1)
3237 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003238
Bram Moolenaar071d4272004-06-13 20:20:40 +00003239 /*
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01003240 * if block 1 becomes full the tree is given an extra level
3241 * The pointers from block 1 are moved into the new block.
3242 * block 1 is updated to point to the new block
3243 * then continue to split the new block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003244 */
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01003245 mch_memmove(pp_new, pp, (size_t)page_size);
3246 pp->pb_count = 1;
3247 pp->pb_pointer[0].pe_bnum = hp_new->bh_bnum;
3248 pp->pb_pointer[0].pe_line_count = buf->b_ml.ml_line_count;
3249 pp->pb_pointer[0].pe_old_lnum = 1;
3250 pp->pb_pointer[0].pe_page_count = 1;
3251 mf_put(mfp, hp, TRUE, FALSE); // release block 1
3252 hp = hp_new; // new block is to be split
3253 pp = pp_new;
3254 CHECK(stack_idx != 0, _("stack_idx should be 0"));
3255 ip->ip_index = 0;
3256 ++stack_idx; // do block 1 again later
3257 }
3258 /*
3259 * move the pointers after the current one to the new block
3260 * If there are none, the new entry will be in the new block.
3261 */
3262 total_moved = pp->pb_count - pb_idx - 1;
3263 if (total_moved)
3264 {
3265 mch_memmove(&pp_new->pb_pointer[0],
Bram Moolenaar071d4272004-06-13 20:20:40 +00003266 &pp->pb_pointer[pb_idx + 1],
3267 (size_t)(total_moved) * sizeof(PTR_EN));
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01003268 pp_new->pb_count = total_moved;
3269 pp->pb_count -= total_moved - 1;
3270 pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right;
3271 pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right;
3272 pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right;
3273 if (lnum_right)
3274 pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003275 }
=?UTF-8?q?Dundar=20G=C3=B6c?=f26c1612022-04-07 13:26:34 +01003276 else
3277 {
3278 pp_new->pb_count = 1;
3279 pp_new->pb_pointer[0].pe_bnum = bnum_right;
3280 pp_new->pb_pointer[0].pe_line_count = line_count_right;
3281 pp_new->pb_pointer[0].pe_page_count = page_count_right;
3282 pp_new->pb_pointer[0].pe_old_lnum = lnum_right;
3283 }
3284 pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
3285 pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
3286 pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
3287 if (lnum_left)
3288 pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left;
3289 lnum_left = 0;
3290 lnum_right = 0;
3291
3292 /*
3293 * recompute line counts
3294 */
3295 line_count_right = 0;
3296 for (i = 0; i < (int)pp_new->pb_count; ++i)
3297 line_count_right += pp_new->pb_pointer[i].pe_line_count;
3298 line_count_left = 0;
3299 for (i = 0; i < (int)pp->pb_count; ++i)
3300 line_count_left += pp->pb_pointer[i].pe_line_count;
3301
3302 bnum_left = hp->bh_bnum;
3303 bnum_right = hp_new->bh_bnum;
3304 page_count_left = 1;
3305 page_count_right = 1;
3306 mf_put(mfp, hp, TRUE, FALSE);
3307 mf_put(mfp, hp_new, TRUE, FALSE);
3308
Bram Moolenaar071d4272004-06-13 20:20:40 +00003309 }
3310
3311 /*
3312 * Safety check: fallen out of for loop?
3313 */
3314 if (stack_idx < 0)
3315 {
Bram Moolenaareaaac012022-01-02 17:00:40 +00003316 iemsg(_(e_updated_too_many_blocks));
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003317 buf->b_ml.ml_stack_top = 0; // invalidate stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00003318 }
3319 }
3320
3321#ifdef FEAT_BYTEOFF
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003322 // The line was inserted below 'lnum'
Bram Moolenaar50652b02022-08-07 21:48:37 +01003323 ml_updatechunk(buf, lnum + 1,
3324# ifdef FEAT_PROP_POPUP
3325 (long)text_len
3326# else
3327 (long)len
Bram Moolenaare5a0e8c2022-08-09 21:37:55 +01003328# endif
Bram Moolenaar50652b02022-08-07 21:48:37 +01003329 , ML_CHNK_ADDLINE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003330#endif
Bram Moolenaarcdd8a5e2021-08-25 16:40:03 +02003331
Bram Moolenaar071d4272004-06-13 20:20:40 +00003332#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02003333 if (netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +00003334 {
3335 if (STRLEN(line) > 0)
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003336 netbeans_inserted(buf, lnum+1, (colnr_T)0, line, (int)STRLEN(line));
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00003337 netbeans_inserted(buf, lnum+1, (colnr_T)STRLEN(line),
Bram Moolenaar071d4272004-06-13 20:20:40 +00003338 (char_u *)"\n", 1);
3339 }
3340#endif
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01003341#ifdef FEAT_JOB_CHANNEL
Bram Moolenaar99ef0622016-03-06 20:22:25 +01003342 if (buf->b_write_to_channel)
3343 channel_write_new_lines(buf);
3344#endif
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003345 ret = OK;
Bram Moolenaar99ef0622016-03-06 20:22:25 +01003346
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003347theend:
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003348#ifdef FEAT_PROP_POPUP
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003349 vim_free(tofree);
3350#endif
3351 return ret;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003352}
3353
3354/*
Bram Moolenaar250e3112019-07-15 23:02:14 +02003355 * Flush any pending change and call ml_append_int()
3356 */
3357 static int
3358ml_append_flush(
3359 buf_T *buf,
3360 linenr_T lnum, // append after this line (can be 0)
3361 char_u *line, // text of the new line
3362 colnr_T len, // length of line, including NUL, or 0
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003363 int flags) // ML_APPEND_ flags
Bram Moolenaar250e3112019-07-15 23:02:14 +02003364{
3365 if (lnum > buf->b_ml.ml_line_count)
3366 return FAIL; // lnum out of range
3367
3368 if (buf->b_ml.ml_line_lnum != 0)
3369 // This may also invoke ml_append_int().
3370 ml_flush_line(buf);
3371
3372#ifdef FEAT_EVAL
3373 // When inserting above recorded changes: flush the changes before changing
3374 // the text. Then flush the cached line, it may become invalid.
3375 may_invoke_listeners(buf, lnum + 1, lnum + 1, 1);
3376 if (buf->b_ml.ml_line_lnum != 0)
3377 ml_flush_line(buf);
3378#endif
3379
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003380 return ml_append_int(buf, lnum, line, len, flags);
Bram Moolenaar250e3112019-07-15 23:02:14 +02003381}
3382
3383/*
3384 * Append a line after lnum (may be 0 to insert a line in front of the file).
3385 * "line" does not need to be allocated, but can't be another line in a
3386 * buffer, unlocking may make it invalid.
3387 *
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003388 * "newfile": TRUE when starting to edit a new file, meaning that pe_old_lnum
Bram Moolenaar250e3112019-07-15 23:02:14 +02003389 * will be set for recovery
3390 * Check: The caller of this function should probably also call
3391 * appended_lines().
3392 *
3393 * return FAIL for failure, OK otherwise
3394 */
3395 int
3396ml_append(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003397 linenr_T lnum, // append after this line (can be 0)
3398 char_u *line, // text of the new line
3399 colnr_T len, // length of new line, including NUL, or 0
3400 int newfile) // flag, see above
Bram Moolenaar250e3112019-07-15 23:02:14 +02003401{
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003402 return ml_append_flags(lnum, line, len, newfile ? ML_APPEND_NEW : 0);
3403}
3404
3405 int
3406ml_append_flags(
3407 linenr_T lnum, // append after this line (can be 0)
3408 char_u *line, // text of the new line
3409 colnr_T len, // length of new line, including NUL, or 0
3410 int flags) // ML_APPEND_ values
3411{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003412 // When starting up, we might still need to create the memfile
Bram Moolenaar250e3112019-07-15 23:02:14 +02003413 if (curbuf->b_ml.ml_mfp == NULL && open_buffer(FALSE, NULL, 0) == FAIL)
3414 return FAIL;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003415 return ml_append_flush(curbuf, lnum, line, len, flags);
Bram Moolenaar250e3112019-07-15 23:02:14 +02003416}
3417
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003418
Bram Moolenaar9f28eeb2022-05-16 10:04:51 +01003419#if defined(FEAT_SPELL) || defined(FEAT_QUICKFIX) || defined(FEAT_PROP_POPUP) \
3420 || defined(PROTO)
Bram Moolenaar250e3112019-07-15 23:02:14 +02003421/*
3422 * Like ml_append() but for an arbitrary buffer. The buffer must already have
3423 * a memline.
3424 */
3425 int
3426ml_append_buf(
3427 buf_T *buf,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003428 linenr_T lnum, // append after this line (can be 0)
3429 char_u *line, // text of the new line
3430 colnr_T len, // length of new line, including NUL, or 0
3431 int newfile) // flag, see above
Bram Moolenaar250e3112019-07-15 23:02:14 +02003432{
3433 if (buf->b_ml.ml_mfp == NULL)
3434 return FAIL;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003435 return ml_append_flush(buf, lnum, line, len, newfile ? ML_APPEND_NEW : 0);
Bram Moolenaar250e3112019-07-15 23:02:14 +02003436}
3437#endif
3438
3439/*
Bram Moolenaar5966ea12020-07-15 15:30:05 +02003440 * Replace line "lnum", with buffering, in current buffer.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003441 *
Bram Moolenaar1056d982006-03-09 22:37:52 +00003442 * If "copy" is TRUE, make a copy of the line, otherwise the line has been
Bram Moolenaar071d4272004-06-13 20:20:40 +00003443 * copied to allocated memory already.
Bram Moolenaar5966ea12020-07-15 15:30:05 +02003444 * If "copy" is FALSE the "line" may be freed to add text properties!
3445 * Do not use it after calling ml_replace().
Bram Moolenaar071d4272004-06-13 20:20:40 +00003446 *
3447 * Check: The caller of this function should probably also call
Bram Moolenaara4d158b2022-08-14 14:17:45 +01003448 * changed_lines(), unless update_screen(UPD_NOT_VALID) is used.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003449 *
3450 * return FAIL for failure, OK otherwise
3451 */
3452 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003453ml_replace(linenr_T lnum, char_u *line, int copy)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003454{
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003455 colnr_T len = -1;
3456
3457 if (line != NULL)
Bram Moolenaar4efe73b2018-12-16 14:37:39 +01003458 len = (colnr_T)STRLEN(line);
Bram Moolenaarccae4672019-01-04 15:09:57 +01003459 return ml_replace_len(lnum, line, len, FALSE, copy);
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003460}
3461
Bram Moolenaarccae4672019-01-04 15:09:57 +01003462/*
3463 * Replace a line for the current buffer. Like ml_replace() with:
3464 * "len_arg" is the length of the text, excluding NUL.
3465 * If "has_props" is TRUE then "line_arg" includes the text properties and
3466 * "len_arg" includes the NUL of the text.
Bram Moolenaarfa4873c2022-06-30 22:13:59 +01003467 * When "copy" is TRUE copy the text into allocated memory, otherwise
3468 * "line_arg" must be allocated and will be consumed here.
Bram Moolenaarccae4672019-01-04 15:09:57 +01003469 */
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003470 int
Bram Moolenaarccae4672019-01-04 15:09:57 +01003471ml_replace_len(
3472 linenr_T lnum,
3473 char_u *line_arg,
3474 colnr_T len_arg,
3475 int has_props,
3476 int copy)
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003477{
3478 char_u *line = line_arg;
3479 colnr_T len = len_arg;
3480
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003481 if (line == NULL) // just checking...
Bram Moolenaar071d4272004-06-13 20:20:40 +00003482 return FAIL;
3483
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003484 // When starting up, we might still need to create the memfile
Bram Moolenaar59f931e2010-07-24 20:27:03 +02003485 if (curbuf->b_ml.ml_mfp == NULL && open_buffer(FALSE, NULL, 0) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003486 return FAIL;
3487
Bram Moolenaarccae4672019-01-04 15:09:57 +01003488 if (!has_props)
3489 ++len; // include the NUL after the text
3490 if (copy)
3491 {
3492 // copy the line to allocated memory
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003493#ifdef FEAT_PROP_POPUP
Bram Moolenaarccae4672019-01-04 15:09:57 +01003494 if (has_props)
3495 line = vim_memsave(line, len);
3496 else
3497#endif
3498 line = vim_strnsave(line, len - 1);
3499 if (line == NULL)
3500 return FAIL;
3501 }
3502
Bram Moolenaar071d4272004-06-13 20:20:40 +00003503#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02003504 if (netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +00003505 {
3506 netbeans_removed(curbuf, lnum, 0, (long)STRLEN(ml_get(lnum)));
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003507 netbeans_inserted(curbuf, lnum, 0, line, (int)STRLEN(line));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003508 }
3509#endif
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003510 if (curbuf->b_ml.ml_line_lnum != lnum)
3511 {
3512 // another line is buffered, flush it
3513 ml_flush_line(curbuf);
3514
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003515#ifdef FEAT_PROP_POPUP
Bram Moolenaarccae4672019-01-04 15:09:57 +01003516 if (curbuf->b_has_textprop && !has_props)
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003517 // Need to fetch the old line to copy over any text properties.
3518 ml_get_buf(curbuf, lnum, TRUE);
3519#endif
3520 }
3521
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003522#ifdef FEAT_PROP_POPUP
Bram Moolenaarccae4672019-01-04 15:09:57 +01003523 if (curbuf->b_has_textprop && !has_props)
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003524 {
3525 size_t oldtextlen = STRLEN(curbuf->b_ml.ml_line_ptr) + 1;
3526
3527 if (oldtextlen < (size_t)curbuf->b_ml.ml_line_len)
3528 {
3529 char_u *newline;
3530 size_t textproplen = curbuf->b_ml.ml_line_len - oldtextlen;
3531
3532 // Need to copy over text properties, stored after the text.
Bram Moolenaarccae4672019-01-04 15:09:57 +01003533 newline = alloc(len + (int)textproplen);
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003534 if (newline != NULL)
3535 {
Bram Moolenaarccae4672019-01-04 15:09:57 +01003536 mch_memmove(newline, line, len);
Bram Moolenaar0d3cb732019-05-18 13:05:18 +02003537 mch_memmove(newline + len, curbuf->b_ml.ml_line_ptr
3538 + oldtextlen, textproplen);
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003539 vim_free(line);
3540 line = newline;
Bram Moolenaar4efe73b2018-12-16 14:37:39 +01003541 len += (colnr_T)textproplen;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003542 }
3543 }
3544 }
3545#endif
3546
Bram Moolenaarfa4873c2022-06-30 22:13:59 +01003547 if (curbuf->b_ml.ml_flags & (ML_LINE_DIRTY | ML_ALLOCATED))
3548 vim_free(curbuf->b_ml.ml_line_ptr); // free allocated line
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003549
Bram Moolenaar071d4272004-06-13 20:20:40 +00003550 curbuf->b_ml.ml_line_ptr = line;
Bram Moolenaarccae4672019-01-04 15:09:57 +01003551 curbuf->b_ml.ml_line_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003552 curbuf->b_ml.ml_line_lnum = lnum;
3553 curbuf->b_ml.ml_flags = (curbuf->b_ml.ml_flags | ML_LINE_DIRTY) & ~ML_EMPTY;
3554
3555 return OK;
3556}
3557
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003558#ifdef FEAT_PROP_POPUP
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003559/*
3560 * Adjust text properties in line "lnum" for a deleted line.
Paul Ollis1bdc60e2022-05-15 22:24:55 +01003561 * When "above" is true this is the line above the deleted line, otherwise this
3562 * is the line below the deleted line.
3563 * "del_props[del_props_len]" are the properties of the deleted line.
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003564 */
3565 static void
3566adjust_text_props_for_delete(
3567 buf_T *buf,
3568 linenr_T lnum,
3569 char_u *del_props,
3570 int del_props_len,
3571 int above)
3572{
3573 int did_get_line = FALSE;
3574 int done_del;
3575 int done_this;
3576 textprop_T prop_del;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003577 bhdr_T *hp;
3578 DATA_BL *dp;
3579 int idx;
3580 int line_start;
3581 long line_size;
3582 int this_props_len;
3583 char_u *text;
3584 size_t textlen;
3585 int found;
3586
3587 for (done_del = 0; done_del < del_props_len; done_del += sizeof(textprop_T))
3588 {
3589 mch_memmove(&prop_del, del_props + done_del, sizeof(textprop_T));
3590 if ((above && (prop_del.tp_flags & TP_FLAG_CONT_PREV)
3591 && !(prop_del.tp_flags & TP_FLAG_CONT_NEXT))
3592 || (!above && (prop_del.tp_flags & TP_FLAG_CONT_NEXT)
3593 && !(prop_del.tp_flags & TP_FLAG_CONT_PREV)))
3594 {
3595 if (!did_get_line)
3596 {
3597 did_get_line = TRUE;
3598 if ((hp = ml_find_line(buf, lnum, ML_FIND)) == NULL)
3599 return;
3600
3601 dp = (DATA_BL *)(hp->bh_data);
3602 idx = lnum - buf->b_ml.ml_locked_low;
3603 line_start = ((dp->db_index[idx]) & DB_INDEX_MASK);
3604 if (idx == 0) // first line in block, text at the end
3605 line_size = dp->db_txt_end - line_start;
3606 else
Bram Moolenaar87be9be2020-05-30 15:32:02 +02003607 line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK)
3608 - line_start;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003609 text = (char_u *)dp + line_start;
3610 textlen = STRLEN(text) + 1;
3611 if ((long)textlen >= line_size)
3612 {
3613 if (above)
3614 internal_error("no text property above deleted line");
3615 else
3616 internal_error("no text property below deleted line");
3617 return;
3618 }
Bram Moolenaar4b7214e2019-01-03 21:55:32 +01003619 this_props_len = line_size - (int)textlen;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003620 }
3621
3622 found = FALSE;
Bram Moolenaar87be9be2020-05-30 15:32:02 +02003623 for (done_this = 0; done_this < this_props_len;
3624 done_this += sizeof(textprop_T))
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003625 {
Bram Moolenaar87be9be2020-05-30 15:32:02 +02003626 int flag = above ? TP_FLAG_CONT_NEXT
3627 : TP_FLAG_CONT_PREV;
3628 textprop_T prop_this;
3629
Paul Ollis1bdc60e2022-05-15 22:24:55 +01003630 mch_memmove(&prop_this, text + textlen + done_this,
Bram Moolenaar87be9be2020-05-30 15:32:02 +02003631 sizeof(textprop_T));
3632 if ((prop_this.tp_flags & flag)
3633 && prop_del.tp_id == prop_this.tp_id
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003634 && prop_del.tp_type == prop_this.tp_type)
3635 {
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003636 found = TRUE;
Bram Moolenaar87be9be2020-05-30 15:32:02 +02003637 prop_this.tp_flags &= ~flag;
Paul Ollis1bdc60e2022-05-15 22:24:55 +01003638 mch_memmove(text + textlen + done_this, &prop_this,
Bram Moolenaar87be9be2020-05-30 15:32:02 +02003639 sizeof(textprop_T));
3640 break;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003641 }
3642 }
3643 if (!found)
3644 {
3645 if (above)
3646 internal_error("text property above deleted line not found");
3647 else
3648 internal_error("text property below deleted line not found");
3649 }
3650
3651 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
3652 }
3653 }
3654}
3655#endif
3656
Bram Moolenaar071d4272004-06-13 20:20:40 +00003657/*
Bram Moolenaar4033c552017-09-16 20:54:51 +02003658 * Delete line "lnum" in the current buffer.
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003659 * When "flags" has ML_DEL_MESSAGE may give a "No lines in buffer" message.
3660 * When "flags" has ML_DEL_UNDO this is called from undo.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003661 *
3662 * return FAIL for failure, OK otherwise
3663 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003664 static int
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003665ml_delete_int(buf_T *buf, linenr_T lnum, int flags)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003666{
3667 bhdr_T *hp;
3668 memfile_T *mfp;
3669 DATA_BL *dp;
3670 PTR_BL *pp;
3671 infoptr_T *ip;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003672 int count; // number of entries in block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003673 int idx;
3674 int stack_idx;
3675 int text_start;
3676 int line_start;
3677 long line_size;
3678 int i;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003679 int ret = FAIL;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003680#ifdef FEAT_PROP_POPUP
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003681 char_u *textprop_save = NULL;
Bram Moolenaare5a0e8c2022-08-09 21:37:55 +01003682 long textprop_len = 0;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003683#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003684
Bram Moolenaar071d4272004-06-13 20:20:40 +00003685 if (lowest_marked && lowest_marked > lnum)
3686 lowest_marked--;
3687
3688/*
3689 * If the file becomes empty the last line is replaced by an empty line.
3690 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003691 if (buf->b_ml.ml_line_count == 1) // file becomes empty
Bram Moolenaar071d4272004-06-13 20:20:40 +00003692 {
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003693 if ((flags & ML_DEL_MESSAGE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003694#ifdef FEAT_NETBEANS_INTG
3695 && !netbeansSuppressNoLines
3696#endif
3697 )
Bram Moolenaar238a5642006-02-21 22:12:05 +00003698 set_keep_msg((char_u *)_(no_lines_msg), 0);
3699
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003700 // FEAT_BYTEOFF already handled in there, don't worry 'bout it below
Bram Moolenaar071d4272004-06-13 20:20:40 +00003701 i = ml_replace((linenr_T)1, (char_u *)"", TRUE);
3702 buf->b_ml.ml_flags |= ML_EMPTY;
3703
3704 return i;
3705 }
3706
3707/*
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003708 * Find the data block containing the line.
3709 * This also fills the stack with the blocks from the root to the data block.
3710 * This also releases any locked block..
Bram Moolenaar071d4272004-06-13 20:20:40 +00003711 */
3712 mfp = buf->b_ml.ml_mfp;
3713 if (mfp == NULL)
3714 return FAIL;
3715
3716 if ((hp = ml_find_line(buf, lnum, ML_DELETE)) == NULL)
3717 return FAIL;
3718
3719 dp = (DATA_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003720 // compute line count before the delete
Bram Moolenaar071d4272004-06-13 20:20:40 +00003721 count = (long)(buf->b_ml.ml_locked_high)
3722 - (long)(buf->b_ml.ml_locked_low) + 2;
3723 idx = lnum - buf->b_ml.ml_locked_low;
3724
3725 --buf->b_ml.ml_line_count;
3726
3727 line_start = ((dp->db_index[idx]) & DB_INDEX_MASK);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003728 if (idx == 0) // first line in block, text at the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00003729 line_size = dp->db_txt_end - line_start;
3730 else
3731 line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK) - line_start;
3732
3733#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02003734 if (netbeans_active())
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00003735 netbeans_removed(buf, lnum, 0, line_size);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003736#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003737#ifdef FEAT_PROP_POPUP
Bram Moolenaare5a0e8c2022-08-09 21:37:55 +01003738 // If there are text properties compute their byte length.
3739 // if needed make a copy, so that we can update properties in preceding and
3740 // following lines.
3741 if (buf->b_has_textprop)
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003742 {
3743 size_t textlen = STRLEN((char_u *)dp + line_start) + 1;
3744
Bram Moolenaare5a0e8c2022-08-09 21:37:55 +01003745 textprop_len = line_size - (long)textlen;
3746 if (!(flags & (ML_DEL_UNDO | ML_DEL_NOPROP)) && textprop_len > 0)
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003747 textprop_save = vim_memsave((char_u *)dp + line_start + textlen,
Bram Moolenaare5a0e8c2022-08-09 21:37:55 +01003748 textprop_len);
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003749 }
3750#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003751
3752/*
3753 * special case: If there is only one line in the data block it becomes empty.
3754 * Then we have to remove the entry, pointing to this data block, from the
3755 * pointer block. If this pointer block also becomes empty, we go up another
3756 * block, and so on, up to the root if necessary.
3757 * The line counts in the pointer blocks have already been adjusted by
3758 * ml_find_line().
3759 */
3760 if (count == 1)
3761 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003762 mf_free(mfp, hp); // free the data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003763 buf->b_ml.ml_locked = NULL;
3764
Bram Moolenaare60acc12011-05-10 16:41:25 +02003765 for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0;
3766 --stack_idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003767 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003768 buf->b_ml.ml_stack_top = 0; // stack is invalid when failing
Bram Moolenaar071d4272004-06-13 20:20:40 +00003769 ip = &(buf->b_ml.ml_stack[stack_idx]);
3770 idx = ip->ip_index;
3771 if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003772 goto theend;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003773 pp = (PTR_BL *)(hp->bh_data); // must be pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003774 if (pp->pb_id != PTR_ID)
3775 {
Bram Moolenaareaaac012022-01-02 17:00:40 +00003776 iemsg(_(e_pointer_block_id_wrong_four));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003777 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003778 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003779 }
3780 count = --(pp->pb_count);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003781 if (count == 0) // the pointer block becomes empty!
Bram Moolenaar071d4272004-06-13 20:20:40 +00003782 mf_free(mfp, hp);
3783 else
3784 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003785 if (count != idx) // move entries after the deleted one
Bram Moolenaar071d4272004-06-13 20:20:40 +00003786 mch_memmove(&pp->pb_pointer[idx], &pp->pb_pointer[idx + 1],
3787 (size_t)(count - idx) * sizeof(PTR_EN));
3788 mf_put(mfp, hp, TRUE, FALSE);
3789
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003790 buf->b_ml.ml_stack_top = stack_idx; // truncate stack
3791 // fix line count for rest of blocks in the stack
Bram Moolenaar6b803a72007-05-06 14:25:46 +00003792 if (buf->b_ml.ml_locked_lineadd != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003793 {
3794 ml_lineadd(buf, buf->b_ml.ml_locked_lineadd);
3795 buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high +=
Bram Moolenaar6b803a72007-05-06 14:25:46 +00003796 buf->b_ml.ml_locked_lineadd;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003797 }
3798 ++(buf->b_ml.ml_stack_top);
3799
3800 break;
3801 }
3802 }
3803 CHECK(stack_idx < 0, _("deleted block 1?"));
3804 }
3805 else
3806 {
3807 /*
3808 * delete the text by moving the next lines forwards
3809 */
3810 text_start = dp->db_txt_start;
3811 mch_memmove((char *)dp + text_start + line_size,
3812 (char *)dp + text_start, (size_t)(line_start - text_start));
3813
3814 /*
3815 * delete the index by moving the next indexes backwards
3816 * Adjust the indexes for the text movement.
3817 */
3818 for (i = idx; i < count - 1; ++i)
3819 dp->db_index[i] = dp->db_index[i + 1] + line_size;
3820
3821 dp->db_free += line_size + INDEX_SIZE;
3822 dp->db_txt_start += line_size;
3823 --(dp->db_line_count);
3824
3825 /*
3826 * mark the block dirty and make sure it is in the file (for recovery)
3827 */
3828 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
3829 }
3830
3831#ifdef FEAT_BYTEOFF
Bram Moolenaarcdd8a5e2021-08-25 16:40:03 +02003832 ml_updatechunk(buf, lnum, line_size
3833# ifdef FEAT_PROP_POPUP
Bram Moolenaare5a0e8c2022-08-09 21:37:55 +01003834 - textprop_len
Bram Moolenaarcdd8a5e2021-08-25 16:40:03 +02003835# endif
Bram Moolenaare5a0e8c2022-08-09 21:37:55 +01003836 , ML_CHNK_DELLINE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003837#endif
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003838 ret = OK;
3839
3840theend:
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003841#ifdef FEAT_PROP_POPUP
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003842 if (textprop_save != NULL)
3843 {
3844 // Adjust text properties in the line above and below.
3845 if (lnum > 1)
Bram Moolenaar4cd5c522021-06-27 13:04:00 +02003846 adjust_text_props_for_delete(buf, lnum - 1, textprop_save,
Bram Moolenaare5a0e8c2022-08-09 21:37:55 +01003847 (int)textprop_len, TRUE);
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003848 if (lnum <= buf->b_ml.ml_line_count)
Bram Moolenaar4cd5c522021-06-27 13:04:00 +02003849 adjust_text_props_for_delete(buf, lnum, textprop_save,
Bram Moolenaare5a0e8c2022-08-09 21:37:55 +01003850 (int)textprop_len, FALSE);
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003851 }
3852 vim_free(textprop_save);
3853#endif
3854 return ret;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003855}
3856
3857/*
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003858 * Delete line "lnum" in the current buffer.
3859 * When "message" is TRUE may give a "No lines in buffer" message.
3860 *
3861 * Check: The caller of this function should probably also call
3862 * deleted_lines() after this.
3863 *
3864 * return FAIL for failure, OK otherwise
3865 */
3866 int
Bram Moolenaarca70c072020-05-30 20:30:46 +02003867ml_delete(linenr_T lnum)
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003868{
Bram Moolenaarca70c072020-05-30 20:30:46 +02003869 return ml_delete_flags(lnum, 0);
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003870}
3871
3872/*
3873 * Like ml_delete() but using flags (see ml_delete_int()).
3874 */
3875 int
3876ml_delete_flags(linenr_T lnum, int flags)
3877{
3878 ml_flush_line(curbuf);
3879 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count)
3880 return FAIL;
3881
3882#ifdef FEAT_EVAL
3883 // When inserting above recorded changes: flush the changes before changing
3884 // the text.
3885 may_invoke_listeners(curbuf, lnum, lnum + 1, -1);
3886#endif
3887
3888 return ml_delete_int(curbuf, lnum, flags);
3889}
3890
3891/*
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003892 * set the DB_MARKED flag for line 'lnum'
Bram Moolenaar071d4272004-06-13 20:20:40 +00003893 */
3894 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003895ml_setmarked(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003896{
3897 bhdr_T *hp;
3898 DATA_BL *dp;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003899 // invalid line number
Bram Moolenaar071d4272004-06-13 20:20:40 +00003900 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count
3901 || curbuf->b_ml.ml_mfp == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003902 return; // give error message?
Bram Moolenaar071d4272004-06-13 20:20:40 +00003903
3904 if (lowest_marked == 0 || lowest_marked > lnum)
3905 lowest_marked = lnum;
3906
3907 /*
3908 * find the data block containing the line
3909 * This also fills the stack with the blocks from the root to the data block
3910 * This also releases any locked block.
3911 */
3912 if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003913 return; // give error message?
Bram Moolenaar071d4272004-06-13 20:20:40 +00003914
3915 dp = (DATA_BL *)(hp->bh_data);
3916 dp->db_index[lnum - curbuf->b_ml.ml_locked_low] |= DB_MARKED;
3917 curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
3918}
3919
3920/*
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003921 * find the first line with its DB_MARKED flag set
Bram Moolenaar071d4272004-06-13 20:20:40 +00003922 */
3923 linenr_T
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003924ml_firstmarked(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003925{
3926 bhdr_T *hp;
3927 DATA_BL *dp;
3928 linenr_T lnum;
3929 int i;
3930
3931 if (curbuf->b_ml.ml_mfp == NULL)
3932 return (linenr_T) 0;
3933
3934 /*
3935 * The search starts with lowest_marked line. This is the last line where
3936 * a mark was found, adjusted by inserting/deleting lines.
3937 */
3938 for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count; )
3939 {
3940 /*
3941 * Find the data block containing the line.
3942 * This also fills the stack with the blocks from the root to the data
3943 * block This also releases any locked block.
3944 */
3945 if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003946 return (linenr_T)0; // give error message?
Bram Moolenaar071d4272004-06-13 20:20:40 +00003947
3948 dp = (DATA_BL *)(hp->bh_data);
3949
3950 for (i = lnum - curbuf->b_ml.ml_locked_low;
3951 lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum)
3952 if ((dp->db_index[i]) & DB_MARKED)
3953 {
3954 (dp->db_index[i]) &= DB_INDEX_MASK;
3955 curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
3956 lowest_marked = lnum + 1;
3957 return lnum;
3958 }
3959 }
3960
3961 return (linenr_T) 0;
3962}
3963
Bram Moolenaar071d4272004-06-13 20:20:40 +00003964/*
3965 * clear all DB_MARKED flags
3966 */
3967 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003968ml_clearmarked(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003969{
3970 bhdr_T *hp;
3971 DATA_BL *dp;
3972 linenr_T lnum;
3973 int i;
3974
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003975 if (curbuf->b_ml.ml_mfp == NULL) // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00003976 return;
3977
3978 /*
3979 * The search starts with line lowest_marked.
3980 */
3981 for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count; )
3982 {
3983 /*
3984 * Find the data block containing the line.
3985 * This also fills the stack with the blocks from the root to the data
3986 * block and releases any locked block.
3987 */
3988 if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003989 return; // give error message?
Bram Moolenaar071d4272004-06-13 20:20:40 +00003990
3991 dp = (DATA_BL *)(hp->bh_data);
3992
3993 for (i = lnum - curbuf->b_ml.ml_locked_low;
3994 lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum)
3995 if ((dp->db_index[i]) & DB_MARKED)
3996 {
3997 (dp->db_index[i]) &= DB_INDEX_MASK;
3998 curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
3999 }
4000 }
4001
4002 lowest_marked = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004003}
4004
4005/*
4006 * flush ml_line if necessary
4007 */
4008 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004009ml_flush_line(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004010{
4011 bhdr_T *hp;
4012 DATA_BL *dp;
4013 linenr_T lnum;
4014 char_u *new_line;
4015 char_u *old_line;
4016 colnr_T new_len;
4017 int old_len;
4018 int extra;
4019 int idx;
4020 int start;
4021 int count;
4022 int i;
Bram Moolenaar0ca4b352010-02-11 18:54:43 +01004023 static int entered = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004024
4025 if (buf->b_ml.ml_line_lnum == 0 || buf->b_ml.ml_mfp == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004026 return; // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00004027
4028 if (buf->b_ml.ml_flags & ML_LINE_DIRTY)
4029 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004030 // This code doesn't work recursively, but Netbeans may call back here
4031 // when obtaining the cursor position.
Bram Moolenaar0ca4b352010-02-11 18:54:43 +01004032 if (entered)
4033 return;
4034 entered = TRUE;
4035
Bram Moolenaar071d4272004-06-13 20:20:40 +00004036 lnum = buf->b_ml.ml_line_lnum;
4037 new_line = buf->b_ml.ml_line_ptr;
4038
4039 hp = ml_find_line(buf, lnum, ML_FIND);
4040 if (hp == NULL)
Bram Moolenaareaaac012022-01-02 17:00:40 +00004041 siemsg(_(e_cannot_find_line_nr), lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004042 else
4043 {
4044 dp = (DATA_BL *)(hp->bh_data);
4045 idx = lnum - buf->b_ml.ml_locked_low;
4046 start = ((dp->db_index[idx]) & DB_INDEX_MASK);
4047 old_line = (char_u *)dp + start;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004048 if (idx == 0) // line is last in block
Bram Moolenaar071d4272004-06-13 20:20:40 +00004049 old_len = dp->db_txt_end - start;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004050 else // text of previous line follows
Bram Moolenaar071d4272004-06-13 20:20:40 +00004051 old_len = (dp->db_index[idx - 1] & DB_INDEX_MASK) - start;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01004052 new_len = buf->b_ml.ml_line_len;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004053 extra = new_len - old_len; // negative if lines gets smaller
Bram Moolenaar071d4272004-06-13 20:20:40 +00004054
4055 /*
4056 * if new line fits in data block, replace directly
4057 */
4058 if ((int)dp->db_free >= extra)
4059 {
Bram Moolenaar92755bb2021-08-15 22:18:04 +02004060#if defined(FEAT_BYTEOFF) && defined(FEAT_PROP_POPUP)
Bram Moolenaar14c75302021-08-15 14:28:40 +02004061 int old_prop_len = 0;
Paul Ollis4c3d21a2022-05-24 21:26:37 +01004062 if (buf->b_has_textprop)
4063 old_prop_len = old_len - (int)STRLEN(old_line) - 1;
Bram Moolenaar14c75302021-08-15 14:28:40 +02004064#endif
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004065 // if the length changes and there are following lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00004066 count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 1;
4067 if (extra != 0 && idx < count - 1)
4068 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004069 // move text of following lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00004070 mch_memmove((char *)dp + dp->db_txt_start - extra,
4071 (char *)dp + dp->db_txt_start,
4072 (size_t)(start - dp->db_txt_start));
4073
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004074 // adjust pointers of this and following lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00004075 for (i = idx + 1; i < count; ++i)
4076 dp->db_index[i] -= extra;
4077 }
4078 dp->db_index[idx] -= extra;
4079
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004080 // adjust free space
Bram Moolenaar071d4272004-06-13 20:20:40 +00004081 dp->db_free -= extra;
4082 dp->db_txt_start -= extra;
4083
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004084 // copy new line into the data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00004085 mch_memmove(old_line - extra, new_line, (size_t)new_len);
4086 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
Bram Moolenaar92755bb2021-08-15 22:18:04 +02004087#if defined(FEAT_BYTEOFF) && defined(FEAT_PROP_POPUP)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004088 // The else case is already covered by the insert and delete
Bram Moolenaar14c75302021-08-15 14:28:40 +02004089 if (buf->b_has_textprop)
4090 {
4091 // Do not count the size of any text properties.
4092 extra += old_prop_len;
Bram Moolenaar434df7a2021-08-16 21:15:32 +02004093 extra -= new_len - (int)STRLEN(new_line) - 1;
Bram Moolenaar14c75302021-08-15 14:28:40 +02004094 }
4095 if (extra != 0)
4096 ml_updatechunk(buf, lnum, (long)extra, ML_CHNK_UPDLINE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004097#endif
4098 }
4099 else
4100 {
4101 /*
4102 * Cannot do it in one data block: Delete and append.
4103 * Append first, because ml_delete_int() cannot delete the
4104 * last line in a buffer, which causes trouble for a buffer
4105 * that has only one line.
4106 * Don't forget to copy the mark!
4107 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004108 // How about handling errors???
Bram Moolenaara9d4b842020-05-30 14:46:52 +02004109 (void)ml_append_int(buf, lnum, new_line, new_len,
Bram Moolenaar840f91f2021-05-26 22:32:10 +02004110 ((dp->db_index[idx] & DB_MARKED) ? ML_APPEND_MARK : 0)
4111#ifdef FEAT_PROP_POPUP
4112 | ML_APPEND_NOPROP
4113#endif
4114 );
Bram Moolenaar4cd5c522021-06-27 13:04:00 +02004115 (void)ml_delete_int(buf, lnum, ML_DEL_NOPROP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004116 }
4117 }
4118 vim_free(new_line);
Bram Moolenaar0ca4b352010-02-11 18:54:43 +01004119
4120 entered = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004121 }
Bram Moolenaarfa4873c2022-06-30 22:13:59 +01004122 else if (buf->b_ml.ml_flags & ML_ALLOCATED)
4123 vim_free(buf->b_ml.ml_line_ptr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004124
Bram Moolenaarfa4873c2022-06-30 22:13:59 +01004125 buf->b_ml.ml_flags &= ~(ML_LINE_DIRTY | ML_ALLOCATED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004126 buf->b_ml.ml_line_lnum = 0;
4127}
4128
4129/*
4130 * create a new, empty, data block
4131 */
4132 static bhdr_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004133ml_new_data(memfile_T *mfp, int negative, int page_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004134{
4135 bhdr_T *hp;
4136 DATA_BL *dp;
4137
4138 if ((hp = mf_new(mfp, negative, page_count)) == NULL)
4139 return NULL;
4140
4141 dp = (DATA_BL *)(hp->bh_data);
4142 dp->db_id = DATA_ID;
4143 dp->db_txt_start = dp->db_txt_end = page_count * mfp->mf_page_size;
4144 dp->db_free = dp->db_txt_start - HEADER_SIZE;
4145 dp->db_line_count = 0;
4146
4147 return hp;
4148}
4149
4150/*
4151 * create a new, empty, pointer block
4152 */
4153 static bhdr_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004154ml_new_ptr(memfile_T *mfp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004155{
4156 bhdr_T *hp;
4157 PTR_BL *pp;
4158
4159 if ((hp = mf_new(mfp, FALSE, 1)) == NULL)
4160 return NULL;
4161
4162 pp = (PTR_BL *)(hp->bh_data);
4163 pp->pb_id = PTR_ID;
4164 pp->pb_count = 0;
Bram Moolenaar20a825a2010-05-31 21:27:30 +02004165 pp->pb_count_max = (short_u)((mfp->mf_page_size - sizeof(PTR_BL))
4166 / sizeof(PTR_EN) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004167
4168 return hp;
4169}
4170
4171/*
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01004172 * Lookup line 'lnum' in a memline.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004173 *
4174 * action: if ML_DELETE or ML_INSERT the line count is updated while searching
4175 * if ML_FLUSH only flush a locked block
4176 * if ML_FIND just find the line
4177 *
4178 * If the block was found it is locked and put in ml_locked.
4179 * The stack is updated to lead to the locked block. The ip_high field in
4180 * the stack is updated to reflect the last line in the block AFTER the
4181 * insert or delete, also if the pointer block has not been updated yet. But
Bram Moolenaar6b803a72007-05-06 14:25:46 +00004182 * if ml_locked != NULL ml_locked_lineadd must be added to ip_high.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004183 *
4184 * return: NULL for failure, pointer to block header otherwise
4185 */
4186 static bhdr_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004187ml_find_line(buf_T *buf, linenr_T lnum, int action)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004188{
4189 DATA_BL *dp;
4190 PTR_BL *pp;
4191 infoptr_T *ip;
4192 bhdr_T *hp;
4193 memfile_T *mfp;
4194 linenr_T t;
4195 blocknr_T bnum, bnum2;
4196 int dirty;
4197 linenr_T low, high;
4198 int top;
4199 int page_count;
4200 int idx;
4201
4202 mfp = buf->b_ml.ml_mfp;
4203
4204 /*
4205 * If there is a locked block check if the wanted line is in it.
4206 * If not, flush and release the locked block.
4207 * Don't do this for ML_INSERT_SAME, because the stack need to be updated.
4208 * Don't do this for ML_FLUSH, because we want to flush the locked block.
Bram Moolenaar47b8b152007-02-07 02:41:57 +00004209 * Don't do this when 'swapfile' is reset, we want to load all the blocks.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004210 */
4211 if (buf->b_ml.ml_locked)
4212 {
Bram Moolenaar47b8b152007-02-07 02:41:57 +00004213 if (ML_SIMPLE(action)
4214 && buf->b_ml.ml_locked_low <= lnum
4215 && buf->b_ml.ml_locked_high >= lnum
4216 && !mf_dont_release)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004217 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004218 // remember to update pointer blocks and stack later
Bram Moolenaar071d4272004-06-13 20:20:40 +00004219 if (action == ML_INSERT)
4220 {
4221 ++(buf->b_ml.ml_locked_lineadd);
4222 ++(buf->b_ml.ml_locked_high);
4223 }
4224 else if (action == ML_DELETE)
4225 {
4226 --(buf->b_ml.ml_locked_lineadd);
4227 --(buf->b_ml.ml_locked_high);
4228 }
4229 return (buf->b_ml.ml_locked);
4230 }
4231
4232 mf_put(mfp, buf->b_ml.ml_locked, buf->b_ml.ml_flags & ML_LOCKED_DIRTY,
4233 buf->b_ml.ml_flags & ML_LOCKED_POS);
4234 buf->b_ml.ml_locked = NULL;
4235
Bram Moolenaar6b803a72007-05-06 14:25:46 +00004236 /*
4237 * If lines have been added or deleted in the locked block, need to
4238 * update the line count in pointer blocks.
4239 */
4240 if (buf->b_ml.ml_locked_lineadd != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004241 ml_lineadd(buf, buf->b_ml.ml_locked_lineadd);
4242 }
4243
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004244 if (action == ML_FLUSH) // nothing else to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00004245 return NULL;
4246
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004247 bnum = 1; // start at the root of the tree
Bram Moolenaar071d4272004-06-13 20:20:40 +00004248 page_count = 1;
4249 low = 1;
4250 high = buf->b_ml.ml_line_count;
4251
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004252 if (action == ML_FIND) // first try stack entries
Bram Moolenaar071d4272004-06-13 20:20:40 +00004253 {
4254 for (top = buf->b_ml.ml_stack_top - 1; top >= 0; --top)
4255 {
4256 ip = &(buf->b_ml.ml_stack[top]);
4257 if (ip->ip_low <= lnum && ip->ip_high >= lnum)
4258 {
4259 bnum = ip->ip_bnum;
4260 low = ip->ip_low;
4261 high = ip->ip_high;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004262 buf->b_ml.ml_stack_top = top; // truncate stack at prev entry
Bram Moolenaar071d4272004-06-13 20:20:40 +00004263 break;
4264 }
4265 }
4266 if (top < 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004267 buf->b_ml.ml_stack_top = 0; // not found, start at the root
Bram Moolenaar071d4272004-06-13 20:20:40 +00004268 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004269 else // ML_DELETE or ML_INSERT
4270 buf->b_ml.ml_stack_top = 0; // start at the root
Bram Moolenaar071d4272004-06-13 20:20:40 +00004271
4272/*
4273 * search downwards in the tree until a data block is found
4274 */
4275 for (;;)
4276 {
4277 if ((hp = mf_get(mfp, bnum, page_count)) == NULL)
4278 goto error_noblock;
4279
4280 /*
4281 * update high for insert/delete
4282 */
4283 if (action == ML_INSERT)
4284 ++high;
4285 else if (action == ML_DELETE)
4286 --high;
4287
4288 dp = (DATA_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004289 if (dp->db_id == DATA_ID) // data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00004290 {
4291 buf->b_ml.ml_locked = hp;
4292 buf->b_ml.ml_locked_low = low;
4293 buf->b_ml.ml_locked_high = high;
4294 buf->b_ml.ml_locked_lineadd = 0;
4295 buf->b_ml.ml_flags &= ~(ML_LOCKED_DIRTY | ML_LOCKED_POS);
4296 return hp;
4297 }
4298
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004299 pp = (PTR_BL *)(dp); // must be pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00004300 if (pp->pb_id != PTR_ID)
4301 {
Bram Moolenaareaaac012022-01-02 17:00:40 +00004302 iemsg(_(e_pointer_block_id_wrong));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004303 goto error_block;
4304 }
4305
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004306 if ((top = ml_add_stack(buf)) < 0) // add new entry to stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00004307 goto error_block;
4308 ip = &(buf->b_ml.ml_stack[top]);
4309 ip->ip_bnum = bnum;
4310 ip->ip_low = low;
4311 ip->ip_high = high;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004312 ip->ip_index = -1; // index not known yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00004313
4314 dirty = FALSE;
4315 for (idx = 0; idx < (int)pp->pb_count; ++idx)
4316 {
4317 t = pp->pb_pointer[idx].pe_line_count;
4318 CHECK(t == 0, _("pe_line_count is zero"));
4319 if ((low += t) > lnum)
4320 {
4321 ip->ip_index = idx;
4322 bnum = pp->pb_pointer[idx].pe_bnum;
4323 page_count = pp->pb_pointer[idx].pe_page_count;
4324 high = low - 1;
4325 low -= t;
4326
4327 /*
4328 * a negative block number may have been changed
4329 */
4330 if (bnum < 0)
4331 {
4332 bnum2 = mf_trans_del(mfp, bnum);
4333 if (bnum != bnum2)
4334 {
4335 bnum = bnum2;
4336 pp->pb_pointer[idx].pe_bnum = bnum;
4337 dirty = TRUE;
4338 }
4339 }
4340
4341 break;
4342 }
4343 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004344 if (idx >= (int)pp->pb_count) // past the end: something wrong!
Bram Moolenaar071d4272004-06-13 20:20:40 +00004345 {
4346 if (lnum > buf->b_ml.ml_line_count)
Bram Moolenaareaaac012022-01-02 17:00:40 +00004347 siemsg(_(e_line_number_out_of_range_nr_past_the_end),
Bram Moolenaar071d4272004-06-13 20:20:40 +00004348 lnum - buf->b_ml.ml_line_count);
4349
4350 else
Bram Moolenaareaaac012022-01-02 17:00:40 +00004351 siemsg(_(e_line_count_wrong_in_block_nr), bnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004352 goto error_block;
4353 }
4354 if (action == ML_DELETE)
4355 {
4356 pp->pb_pointer[idx].pe_line_count--;
4357 dirty = TRUE;
4358 }
4359 else if (action == ML_INSERT)
4360 {
4361 pp->pb_pointer[idx].pe_line_count++;
4362 dirty = TRUE;
4363 }
4364 mf_put(mfp, hp, dirty, FALSE);
4365 }
4366
4367error_block:
4368 mf_put(mfp, hp, FALSE, FALSE);
4369error_noblock:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02004370 /*
4371 * If action is ML_DELETE or ML_INSERT we have to correct the tree for
4372 * the incremented/decremented line counts, because there won't be a line
4373 * inserted/deleted after all.
4374 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004375 if (action == ML_DELETE)
4376 ml_lineadd(buf, 1);
4377 else if (action == ML_INSERT)
4378 ml_lineadd(buf, -1);
4379 buf->b_ml.ml_stack_top = 0;
4380 return NULL;
4381}
4382
4383/*
4384 * add an entry to the info pointer stack
4385 *
4386 * return -1 for failure, number of the new entry otherwise
4387 */
4388 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004389ml_add_stack(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004390{
4391 int top;
4392 infoptr_T *newstack;
4393
4394 top = buf->b_ml.ml_stack_top;
4395
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004396 // may have to increase the stack size
Bram Moolenaar071d4272004-06-13 20:20:40 +00004397 if (top == buf->b_ml.ml_stack_size)
4398 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004399 CHECK(top > 0, _("Stack size increases")); // more than 5 levels???
Bram Moolenaar071d4272004-06-13 20:20:40 +00004400
Bram Moolenaarc799fe22019-05-28 23:08:19 +02004401 newstack = ALLOC_MULT(infoptr_T, buf->b_ml.ml_stack_size + STACK_INCR);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004402 if (newstack == NULL)
4403 return -1;
Bram Moolenaarfbd302f2015-08-08 18:23:46 +02004404 if (top > 0)
4405 mch_memmove(newstack, buf->b_ml.ml_stack,
Bram Moolenaar8c8de832008-06-24 22:58:06 +00004406 (size_t)top * sizeof(infoptr_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004407 vim_free(buf->b_ml.ml_stack);
4408 buf->b_ml.ml_stack = newstack;
4409 buf->b_ml.ml_stack_size += STACK_INCR;
4410 }
4411
4412 buf->b_ml.ml_stack_top++;
4413 return top;
4414}
4415
4416/*
4417 * Update the pointer blocks on the stack for inserted/deleted lines.
4418 * The stack itself is also updated.
4419 *
4420 * When a insert/delete line action fails, the line is not inserted/deleted,
4421 * but the pointer blocks have already been updated. That is fixed here by
4422 * walking through the stack.
4423 *
4424 * Count is the number of lines added, negative if lines have been deleted.
4425 */
4426 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004427ml_lineadd(buf_T *buf, int count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004428{
4429 int idx;
4430 infoptr_T *ip;
4431 PTR_BL *pp;
4432 memfile_T *mfp = buf->b_ml.ml_mfp;
4433 bhdr_T *hp;
4434
4435 for (idx = buf->b_ml.ml_stack_top - 1; idx >= 0; --idx)
4436 {
4437 ip = &(buf->b_ml.ml_stack[idx]);
4438 if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
4439 break;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004440 pp = (PTR_BL *)(hp->bh_data); // must be pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00004441 if (pp->pb_id != PTR_ID)
4442 {
4443 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaareaaac012022-01-02 17:00:40 +00004444 iemsg(_(e_pointer_block_id_wrong_two));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004445 break;
4446 }
4447 pp->pb_pointer[ip->ip_index].pe_line_count += count;
4448 ip->ip_high += count;
4449 mf_put(mfp, hp, TRUE, FALSE);
4450 }
4451}
4452
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004453#if defined(HAVE_READLINK) || defined(PROTO)
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004454/*
4455 * Resolve a symlink in the last component of a file name.
4456 * Note that f_resolve() does it for every part of the path, we don't do that
4457 * here.
4458 * If it worked returns OK and the resolved link in "buf[MAXPATHL]".
4459 * Otherwise returns FAIL.
4460 */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004461 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004462resolve_symlink(char_u *fname, char_u *buf)
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004463{
4464 char_u tmp[MAXPATHL];
4465 int ret;
4466 int depth = 0;
4467
4468 if (fname == NULL)
4469 return FAIL;
4470
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004471 // Put the result so far in tmp[], starting with the original name.
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004472 vim_strncpy(tmp, fname, MAXPATHL - 1);
4473
4474 for (;;)
4475 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004476 // Limit symlink depth to 100, catch recursive loops.
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004477 if (++depth == 100)
4478 {
Bram Moolenaar677658a2022-01-05 16:09:06 +00004479 semsg(_(e_symlink_loop_for_str), fname);
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004480 return FAIL;
4481 }
4482
4483 ret = readlink((char *)tmp, (char *)buf, MAXPATHL - 1);
4484 if (ret <= 0)
4485 {
Bram Moolenaarcc984262005-12-23 22:19:46 +00004486 if (errno == EINVAL || errno == ENOENT)
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004487 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004488 // Found non-symlink or not existing file, stop here.
4489 // When at the first level use the unmodified name, skip the
4490 // call to vim_FullName().
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004491 if (depth == 1)
4492 return FAIL;
4493
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004494 // Use the resolved name in tmp[].
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004495 break;
4496 }
4497
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004498 // There must be some error reading links, use original name.
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004499 return FAIL;
4500 }
4501 buf[ret] = NUL;
4502
4503 /*
4504 * Check whether the symlink is relative or absolute.
4505 * If it's relative, build a new path based on the directory
4506 * portion of the filename (if any) and the path the symlink
4507 * points to.
4508 */
4509 if (mch_isFullName(buf))
4510 STRCPY(tmp, buf);
4511 else
4512 {
4513 char_u *tail;
4514
4515 tail = gettail(tmp);
4516 if (STRLEN(tail) + STRLEN(buf) >= MAXPATHL)
4517 return FAIL;
4518 STRCPY(tail, buf);
4519 }
4520 }
4521
4522 /*
4523 * Try to resolve the full name of the file so that the swapfile name will
4524 * be consistent even when opening a relative symlink from different
4525 * working directories.
4526 */
4527 return vim_FullName(tmp, buf, MAXPATHL, TRUE);
4528}
4529#endif
4530
Bram Moolenaar071d4272004-06-13 20:20:40 +00004531/*
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004532 * Make swap file name out of the file name and a directory name.
4533 * Returns pointer to allocated memory or NULL.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004534 */
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004535 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004536makeswapname(
4537 char_u *fname,
4538 char_u *ffname UNUSED,
4539 buf_T *buf,
4540 char_u *dir_name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004541{
4542 char_u *r, *s;
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02004543 char_u *fname_res = fname;
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004544#ifdef HAVE_READLINK
4545 char_u fname_buf[MAXPATHL];
Bram Moolenaar5966ea12020-07-15 15:30:05 +02004546
4547 // Expand symlink in the file name, so that we put the swap file with the
4548 // actual file instead of with the symlink.
4549 if (resolve_symlink(fname, fname_buf) == OK)
4550 fname_res = fname_buf;
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004551#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004552
Bram Moolenaar4f974752019-02-17 17:44:42 +01004553#if defined(UNIX) || defined(MSWIN) // Need _very_ long file names
Bram Moolenaarb113c3a2017-02-28 21:26:17 +01004554 int len = (int)STRLEN(dir_name);
Bram Moolenaarc525e3a2017-02-18 16:59:02 +01004555
4556 s = dir_name + len;
4557 if (after_pathsep(dir_name, s) && len > 1 && s[-1] == s[-2])
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004558 { // Ends with '//', Use Full path
Bram Moolenaar071d4272004-06-13 20:20:40 +00004559 r = NULL;
Bram Moolenaar5966ea12020-07-15 15:30:05 +02004560 if ((s = make_percent_swname(dir_name, fname_res)) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004561 {
4562 r = modname(s, (char_u *)".swp", FALSE);
4563 vim_free(s);
4564 }
4565 return r;
4566 }
4567#endif
4568
4569 r = buf_modname(
Bram Moolenaar071d4272004-06-13 20:20:40 +00004570 (buf->b_p_sn || buf->b_shortname),
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004571 fname_res,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004572 (char_u *)
Bram Moolenaare60acc12011-05-10 16:41:25 +02004573#if defined(VMS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004574 "_swp",
4575#else
4576 ".swp",
4577#endif
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004578 // Prepend a '.' to the swap file name for the current directory.
Bram Moolenaar48e330a2016-02-23 14:53:34 +01004579 dir_name[0] == '.' && dir_name[1] == NUL);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004580 if (r == NULL) // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00004581 return NULL;
4582
4583 s = get_file_in_dir(r, dir_name);
4584 vim_free(r);
4585 return s;
4586}
4587
4588/*
4589 * Get file name to use for swap file or backup file.
4590 * Use the name of the edited file "fname" and an entry in the 'dir' or 'bdir'
4591 * option "dname".
4592 * - If "dname" is ".", return "fname" (swap file in dir of file).
4593 * - If "dname" starts with "./", insert "dname" in "fname" (swap file
4594 * relative to dir of file).
4595 * - Otherwise, prepend "dname" to the tail of "fname" (swap file in specific
4596 * dir).
4597 *
4598 * The return value is an allocated string and can be NULL.
4599 */
4600 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004601get_file_in_dir(
4602 char_u *fname,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004603 char_u *dname) // don't use "dirname", it is a global for Alpha
Bram Moolenaar071d4272004-06-13 20:20:40 +00004604{
4605 char_u *t;
4606 char_u *tail;
4607 char_u *retval;
4608 int save_char;
4609
4610 tail = gettail(fname);
4611
4612 if (dname[0] == '.' && dname[1] == NUL)
4613 retval = vim_strsave(fname);
4614 else if (dname[0] == '.' && vim_ispathsep(dname[1]))
4615 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004616 if (tail == fname) // no path before file name
Bram Moolenaar071d4272004-06-13 20:20:40 +00004617 retval = concat_fnames(dname + 2, tail, TRUE);
4618 else
4619 {
4620 save_char = *tail;
4621 *tail = NUL;
4622 t = concat_fnames(fname, dname + 2, TRUE);
4623 *tail = save_char;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004624 if (t == NULL) // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00004625 retval = NULL;
4626 else
4627 {
4628 retval = concat_fnames(t, tail, TRUE);
4629 vim_free(t);
4630 }
4631 }
4632 }
4633 else
4634 retval = concat_fnames(dname, tail, TRUE);
4635
Bram Moolenaar4f974752019-02-17 17:44:42 +01004636#ifdef MSWIN
Bram Moolenaar69c35002013-11-04 02:54:12 +01004637 if (retval != NULL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01004638 for (t = gettail(retval); *t != NUL; MB_PTR_ADV(t))
Bram Moolenaar69c35002013-11-04 02:54:12 +01004639 if (*t == ':')
4640 *t = '%';
4641#endif
4642
Bram Moolenaar071d4272004-06-13 20:20:40 +00004643 return retval;
4644}
4645
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004646/*
4647 * Print the ATTENTION message: info about an existing swap file.
4648 */
4649 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004650attention_message(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004651 buf_T *buf, // buffer being edited
4652 char_u *fname) // swap file name
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004653{
Bram Moolenaar8767f522016-07-01 17:17:39 +02004654 stat_T st;
Bram Moolenaar63d25552019-05-10 21:28:38 +02004655 time_t swap_mtime;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004656
4657 ++no_wait_return;
Bram Moolenaareaaac012022-01-02 17:00:40 +00004658 (void)emsg(_(e_attention));
Bram Moolenaar32526b32019-01-19 17:43:09 +01004659 msg_puts(_("\nFound a swap file by the name \""));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004660 msg_home_replace(fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004661 msg_puts("\"\n");
Bram Moolenaar63d25552019-05-10 21:28:38 +02004662 swap_mtime = swapfile_info(fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004663 msg_puts(_("While opening file \""));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004664 msg_outtrans(buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004665 msg_puts("\"\n");
Bram Moolenaard6105cb2018-10-13 19:06:27 +02004666 if (mch_stat((char *)buf->b_fname, &st) == -1)
4667 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004668 msg_puts(_(" CANNOT BE FOUND"));
Bram Moolenaard6105cb2018-10-13 19:06:27 +02004669 }
4670 else
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004671 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004672 msg_puts(_(" dated: "));
Bram Moolenaar63d25552019-05-10 21:28:38 +02004673 msg_puts(get_ctime(st.st_mtime, TRUE));
4674 if (swap_mtime != 0 && st.st_mtime > swap_mtime)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004675 msg_puts(_(" NEWER than swap file!\n"));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004676 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004677 // Some of these messages are long to allow translation to
4678 // other languages.
Bram Moolenaar32526b32019-01-19 17:43:09 +01004679 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"));
4680 msg_puts(_("(2) An edit session for this file crashed.\n"));
4681 msg_puts(_(" If this is the case, use \":recover\" or \"vim -r "));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004682 msg_outtrans(buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004683 msg_puts(_("\"\n to recover the changes (see \":help recovery\").\n"));
4684 msg_puts(_(" If you did this already, delete the swap file \""));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004685 msg_outtrans(fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004686 msg_puts(_("\"\n to avoid this message.\n"));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004687 cmdline_row = msg_row;
4688 --no_wait_return;
4689}
4690
Bram Moolenaar188639d2022-04-04 16:57:21 +01004691typedef enum {
4692 SEA_CHOICE_NONE = 0,
4693 SEA_CHOICE_READONLY = 1,
4694 SEA_CHOICE_EDIT = 2,
4695 SEA_CHOICE_RECOVER = 3,
4696 SEA_CHOICE_DELETE = 4,
4697 SEA_CHOICE_QUIT = 5,
4698 SEA_CHOICE_ABORT = 6
4699} sea_choice_T;
4700
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01004701#if defined(FEAT_EVAL)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004702/*
4703 * Trigger the SwapExists autocommands.
Bram Moolenaar188639d2022-04-04 16:57:21 +01004704 * Returns a value for equivalent to do_dialog().
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004705 */
Bram Moolenaar188639d2022-04-04 16:57:21 +01004706 static sea_choice_T
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004707do_swapexists(buf_T *buf, char_u *fname)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004708{
4709 set_vim_var_string(VV_SWAPNAME, fname, -1);
4710 set_vim_var_string(VV_SWAPCHOICE, NULL, -1);
4711
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004712 // Trigger SwapExists autocommands with <afile> set to the file being
4713 // edited. Disallow changing directory here.
Bram Moolenaar12c22ce2009-04-22 13:58:46 +00004714 ++allbuf_lock;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004715 apply_autocmds(EVENT_SWAPEXISTS, buf->b_fname, NULL, FALSE, NULL);
Bram Moolenaar12c22ce2009-04-22 13:58:46 +00004716 --allbuf_lock;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004717
4718 set_vim_var_string(VV_SWAPNAME, NULL, -1);
4719
4720 switch (*get_vim_var_str(VV_SWAPCHOICE))
4721 {
Bram Moolenaar188639d2022-04-04 16:57:21 +01004722 case 'o': return SEA_CHOICE_READONLY;
4723 case 'e': return SEA_CHOICE_EDIT;
4724 case 'r': return SEA_CHOICE_RECOVER;
4725 case 'd': return SEA_CHOICE_DELETE;
4726 case 'q': return SEA_CHOICE_QUIT;
4727 case 'a': return SEA_CHOICE_ABORT;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004728 }
4729
Bram Moolenaar188639d2022-04-04 16:57:21 +01004730 return SEA_CHOICE_NONE;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004731}
4732#endif
4733
Bram Moolenaar071d4272004-06-13 20:20:40 +00004734/*
4735 * Find out what name to use for the swap file for buffer 'buf'.
4736 *
4737 * Several names are tried to find one that does not exist
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004738 * Returns the name in allocated memory or NULL.
Bram Moolenaarf541c362011-10-26 11:44:18 +02004739 * When out of memory "dirp" is set to NULL.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004740 *
4741 * Note: If BASENAMELEN is not correct, you will get error messages for
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004742 * not being able to open the swap or undo file
Bram Moolenaar12c22ce2009-04-22 13:58:46 +00004743 * Note: May trigger SwapExists autocmd, pointers may change!
Bram Moolenaar071d4272004-06-13 20:20:40 +00004744 */
4745 static char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004746findswapname(
4747 buf_T *buf,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004748 char_u **dirp, // pointer to list of directories
4749 char_u *old_fname) // don't give warning for this file name
Bram Moolenaar071d4272004-06-13 20:20:40 +00004750{
4751 char_u *fname;
4752 int n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004753 char_u *dir_name;
4754#ifdef AMIGA
4755 BPTR fh;
4756#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004757 int r;
Bram Moolenaar69c35002013-11-04 02:54:12 +01004758 char_u *buf_fname = buf->b_fname;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004759
Bram Moolenaar48e330a2016-02-23 14:53:34 +01004760#if !defined(UNIX)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004761# define CREATE_DUMMY_FILE
4762 FILE *dummyfd = NULL;
4763
Bram Moolenaar4f974752019-02-17 17:44:42 +01004764# ifdef MSWIN
Bram Moolenaar69c35002013-11-04 02:54:12 +01004765 if (buf_fname != NULL && !mch_isFullName(buf_fname)
4766 && vim_strchr(gettail(buf_fname), ':'))
4767 {
4768 char_u *t;
4769
4770 buf_fname = vim_strsave(buf_fname);
4771 if (buf_fname == NULL)
4772 buf_fname = buf->b_fname;
4773 else
Bram Moolenaar91acfff2017-03-12 19:22:36 +01004774 for (t = gettail(buf_fname); *t != NUL; MB_PTR_ADV(t))
Bram Moolenaar69c35002013-11-04 02:54:12 +01004775 if (*t == ':')
4776 *t = '%';
4777 }
4778# endif
4779
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004780 /*
4781 * If we start editing a new file, e.g. "test.doc", which resides on an
4782 * MSDOS compatible filesystem, it is possible that the file
4783 * "test.doc.swp" which we create will be exactly the same file. To avoid
4784 * this problem we temporarily create "test.doc". Don't do this when the
Dominique Pelleaf4a61a2021-12-27 17:21:41 +00004785 * check below for an 8.3 file name is used.
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004786 */
Bram Moolenaar69c35002013-11-04 02:54:12 +01004787 if (!(buf->b_p_sn || buf->b_shortname) && buf_fname != NULL
4788 && mch_getperm(buf_fname) < 0)
4789 dummyfd = mch_fopen((char *)buf_fname, "w");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004790#endif
4791
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004792 /*
4793 * Isolate a directory name from *dirp and put it in dir_name.
4794 * First allocate some memory to put the directory name in.
4795 */
Bram Moolenaar964b3742019-05-24 18:54:09 +02004796 dir_name = alloc(STRLEN(*dirp) + 1);
Bram Moolenaarf541c362011-10-26 11:44:18 +02004797 if (dir_name == NULL)
4798 *dirp = NULL;
4799 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004800 (void)copy_option_part(dirp, dir_name, 31000, ",");
4801
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004802 /*
4803 * we try different names until we find one that does not exist yet
4804 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004805 if (dir_name == NULL) // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00004806 fname = NULL;
4807 else
Bram Moolenaar69c35002013-11-04 02:54:12 +01004808 fname = makeswapname(buf_fname, buf->b_ffname, buf, dir_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004809
4810 for (;;)
4811 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004812 if (fname == NULL) // must be out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00004813 break;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004814 if ((n = (int)STRLEN(fname)) == 0) // safety check
Bram Moolenaar071d4272004-06-13 20:20:40 +00004815 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01004816 VIM_CLEAR(fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004817 break;
4818 }
Bram Moolenaar48e330a2016-02-23 14:53:34 +01004819#if defined(UNIX)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004820/*
4821 * Some systems have a MS-DOS compatible filesystem that use 8.3 character
4822 * file names. If this is the first try and the swap file name does not fit in
4823 * 8.3, detect if this is the case, set shortname and try again.
4824 */
4825 if (fname[n - 2] == 'w' && fname[n - 1] == 'p'
4826 && !(buf->b_p_sn || buf->b_shortname))
4827 {
4828 char_u *tail;
4829 char_u *fname2;
Bram Moolenaar8767f522016-07-01 17:17:39 +02004830 stat_T s1, s2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004831 int f1, f2;
4832 int created1 = FALSE, created2 = FALSE;
4833 int same = FALSE;
4834
4835 /*
4836 * Check if swapfile name does not fit in 8.3:
4837 * It either contains two dots, is longer than 8 chars, or starts
4838 * with a dot.
4839 */
Bram Moolenaar69c35002013-11-04 02:54:12 +01004840 tail = gettail(buf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004841 if ( vim_strchr(tail, '.') != NULL
4842 || STRLEN(tail) > (size_t)8
4843 || *gettail(fname) == '.')
4844 {
4845 fname2 = alloc(n + 2);
4846 if (fname2 != NULL)
4847 {
4848 STRCPY(fname2, fname);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004849 // if fname == "xx.xx.swp", fname2 = "xx.xx.swx"
4850 // if fname == ".xx.swp", fname2 = ".xx.swpx"
4851 // if fname == "123456789.swp", fname2 = "12345678x.swp"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004852 if (vim_strchr(tail, '.') != NULL)
4853 fname2[n - 1] = 'x';
4854 else if (*gettail(fname) == '.')
4855 {
4856 fname2[n] = 'x';
4857 fname2[n + 1] = NUL;
4858 }
4859 else
4860 fname2[n - 5] += 1;
4861 /*
4862 * may need to create the files to be able to use mch_stat()
4863 */
4864 f1 = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
4865 if (f1 < 0)
4866 {
4867 f1 = mch_open_rw((char *)fname,
4868 O_RDWR|O_CREAT|O_EXCL|O_EXTRA);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004869 created1 = TRUE;
4870 }
4871 if (f1 >= 0)
4872 {
4873 f2 = mch_open((char *)fname2, O_RDONLY | O_EXTRA, 0);
4874 if (f2 < 0)
4875 {
4876 f2 = mch_open_rw((char *)fname2,
4877 O_RDWR|O_CREAT|O_EXCL|O_EXTRA);
4878 created2 = TRUE;
4879 }
4880 if (f2 >= 0)
4881 {
4882 /*
4883 * Both files exist now. If mch_stat() returns the
4884 * same device and inode they are the same file.
4885 */
4886 if (mch_fstat(f1, &s1) != -1
4887 && mch_fstat(f2, &s2) != -1
4888 && s1.st_dev == s2.st_dev
4889 && s1.st_ino == s2.st_ino)
4890 same = TRUE;
4891 close(f2);
4892 if (created2)
4893 mch_remove(fname2);
4894 }
4895 close(f1);
4896 if (created1)
4897 mch_remove(fname);
4898 }
4899 vim_free(fname2);
4900 if (same)
4901 {
4902 buf->b_shortname = TRUE;
4903 vim_free(fname);
Bram Moolenaar69c35002013-11-04 02:54:12 +01004904 fname = makeswapname(buf_fname, buf->b_ffname,
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004905 buf, dir_name);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004906 continue; // try again with b_shortname set
Bram Moolenaar071d4272004-06-13 20:20:40 +00004907 }
4908 }
4909 }
4910 }
4911#endif
4912 /*
4913 * check if the swapfile already exists
4914 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004915 if (mch_getperm(fname) < 0) // it does not exist
Bram Moolenaar071d4272004-06-13 20:20:40 +00004916 {
4917#ifdef HAVE_LSTAT
Bram Moolenaar8767f522016-07-01 17:17:39 +02004918 stat_T sb;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004919
4920 /*
4921 * Extra security check: When a swap file is a symbolic link, this
4922 * is most likely a symlink attack.
4923 */
4924 if (mch_lstat((char *)fname, &sb) < 0)
4925#else
4926# ifdef AMIGA
4927 fh = Open((UBYTE *)fname, (long)MODE_NEWFILE);
4928 /*
4929 * on the Amiga mch_getperm() will return -1 when the file exists
4930 * but is being used by another program. This happens if you edit
4931 * a file twice.
4932 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004933 if (fh != (BPTR)NULL) // can open file, OK
Bram Moolenaar071d4272004-06-13 20:20:40 +00004934 {
4935 Close(fh);
4936 mch_remove(fname);
4937 break;
4938 }
4939 if (IoErr() != ERROR_OBJECT_IN_USE
4940 && IoErr() != ERROR_OBJECT_EXISTS)
4941# endif
4942#endif
4943 break;
4944 }
4945
4946 /*
4947 * A file name equal to old_fname is OK to use.
4948 */
4949 if (old_fname != NULL && fnamecmp(fname, old_fname) == 0)
4950 break;
4951
4952 /*
4953 * get here when file already exists
4954 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004955 if (fname[n - 2] == 'w' && fname[n - 1] == 'p') // first try
Bram Moolenaar071d4272004-06-13 20:20:40 +00004956 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00004957 /*
4958 * on MS-DOS compatible filesystems (e.g. messydos) file.doc.swp
4959 * and file.doc are the same file. To guess if this problem is
4960 * present try if file.doc.swx exists. If it does, we set
4961 * buf->b_shortname and try file_doc.swp (dots replaced by
4962 * underscores for this file), and try again. If it doesn't we
4963 * assume that "file.doc.swp" already exists.
4964 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004965 if (!(buf->b_p_sn || buf->b_shortname)) // not tried yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00004966 {
4967 fname[n - 1] = 'x';
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004968 r = mch_getperm(fname); // try "file.swx"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004969 fname[n - 1] = 'p';
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004970 if (r >= 0) // "file.swx" seems to exist
Bram Moolenaar071d4272004-06-13 20:20:40 +00004971 {
4972 buf->b_shortname = TRUE;
4973 vim_free(fname);
Bram Moolenaar69c35002013-11-04 02:54:12 +01004974 fname = makeswapname(buf_fname, buf->b_ffname,
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004975 buf, dir_name);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004976 continue; // try again with '.' replaced with '_'
Bram Moolenaar071d4272004-06-13 20:20:40 +00004977 }
4978 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004979 /*
4980 * If we get here the ".swp" file really exists.
4981 * Give an error message, unless recovering, no file name, we are
4982 * viewing a help file or when the path of the file is different
4983 * (happens when all .swp files are in one directory).
4984 */
Bram Moolenaar69c35002013-11-04 02:54:12 +01004985 if (!recoverymode && buf_fname != NULL
Bram Moolenaar2debf1c2019-08-04 20:44:19 +02004986 && !buf->b_help
4987 && !(buf->b_flags & (BF_DUMMY | BF_NO_SEA)))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004988 {
4989 int fd;
4990 struct block0 b0;
4991 int differ = FALSE;
4992
4993 /*
4994 * Try to read block 0 from the swap file to get the original
4995 * file name (and inode number).
4996 */
4997 fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
4998 if (fd >= 0)
4999 {
Bram Moolenaar540fc6f2010-12-17 16:27:16 +01005000 if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005001 {
5002 /*
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005003 * If the swapfile has the same directory as the
5004 * buffer don't compare the directory names, they can
5005 * have a different mountpoint.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005006 */
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005007 if (b0.b0_flags & B0_SAME_DIR)
5008 {
5009 if (fnamecmp(gettail(buf->b_ffname),
5010 gettail(b0.b0_fname)) != 0
5011 || !same_directory(fname, buf->b_ffname))
Bram Moolenaar900b4d72005-12-12 22:05:50 +00005012 {
5013#ifdef CHECK_INODE
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005014 // Symlinks may point to the same file even
5015 // when the name differs, need to check the
5016 // inode too.
Bram Moolenaar900b4d72005-12-12 22:05:50 +00005017 expand_env(b0.b0_fname, NameBuff, MAXPATHL);
5018 if (fnamecmp_ino(buf->b_ffname, NameBuff,
5019 char_to_long(b0.b0_ino)))
5020#endif
5021 differ = TRUE;
5022 }
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005023 }
5024 else
5025 {
5026 /*
5027 * The name in the swap file may be
5028 * "~user/path/file". Expand it first.
5029 */
5030 expand_env(b0.b0_fname, NameBuff, MAXPATHL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005031#ifdef CHECK_INODE
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005032 if (fnamecmp_ino(buf->b_ffname, NameBuff,
Bram Moolenaar900b4d72005-12-12 22:05:50 +00005033 char_to_long(b0.b0_ino)))
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005034 differ = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005035#else
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005036 if (fnamecmp(NameBuff, buf->b_ffname) != 0)
5037 differ = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005038#endif
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005039 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005040 }
5041 close(fd);
5042 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005043
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005044 // give the ATTENTION message when there is an old swap file
5045 // for the current file, and the buffer was not recovered.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005046 if (differ == FALSE && !(curbuf->b_flags & BF_RECOVERED)
5047 && vim_strchr(p_shm, SHM_ATTENTION) == NULL)
5048 {
Bram Moolenaar188639d2022-04-04 16:57:21 +01005049 sea_choice_T choice = SEA_CHOICE_NONE;
5050 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005051#ifdef CREATE_DUMMY_FILE
Bram Moolenaar188639d2022-04-04 16:57:21 +01005052 int did_use_dummy = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005053
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005054 // Avoid getting a warning for the file being created
5055 // outside of Vim, it was created at the start of this
5056 // function. Delete the file now, because Vim might exit
5057 // here if the window is closed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005058 if (dummyfd != NULL)
5059 {
5060 fclose(dummyfd);
5061 dummyfd = NULL;
Bram Moolenaar69c35002013-11-04 02:54:12 +01005062 mch_remove(buf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005063 did_use_dummy = TRUE;
5064 }
5065#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005066
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02005067#ifdef HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar071d4272004-06-13 20:20:40 +00005068 process_still_running = FALSE;
5069#endif
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02005070 // It's safe to delete the swap file if all these are true:
5071 // - the edited file exists
5072 // - the swap file has no changes and looks OK
5073 if (mch_stat((char *)buf->b_fname, &st) == 0
5074 && swapfile_unchanged(fname))
5075 {
Bram Moolenaar188639d2022-04-04 16:57:21 +01005076 choice = SEA_CHOICE_DELETE;
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02005077 if (p_verbose > 0)
5078 verb_msg(_("Found a swap file that is not useful, deleting it"));
5079 }
5080
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01005081#if defined(FEAT_EVAL)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005082 /*
5083 * If there is an SwapExists autocommand and we can handle
5084 * the response, trigger it. It may return 0 to ask the
5085 * user anyway.
5086 */
Bram Moolenaar188639d2022-04-04 16:57:21 +01005087 if (choice == SEA_CHOICE_NONE
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02005088 && swap_exists_action != SEA_NONE
Bram Moolenaar69c35002013-11-04 02:54:12 +01005089 && has_autocmd(EVENT_SWAPEXISTS, buf_fname, buf))
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005090 choice = do_swapexists(buf, fname);
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005091#endif
Bram Moolenaar188639d2022-04-04 16:57:21 +01005092
5093 if (choice == SEA_CHOICE_NONE
5094 && swap_exists_action == SEA_READONLY)
5095 {
5096 // always open readonly.
5097 choice = SEA_CHOICE_READONLY;
5098 }
5099
5100 if (choice == SEA_CHOICE_NONE)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005101 {
5102#ifdef FEAT_GUI
Bram Moolenaar798184c2018-10-07 20:48:39 +02005103 // If we are supposed to start the GUI but it wasn't
5104 // completely started yet, start it now. This makes
5105 // the messages displayed in the Vim window when
5106 // loading a session from the .gvimrc file.
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005107 if (gui.starting && !gui.in_use)
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005108 gui_start(NULL);
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005109#endif
Bram Moolenaar798184c2018-10-07 20:48:39 +02005110 // Show info about the existing swap file.
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005111 attention_message(buf, fname);
5112
Bram Moolenaar798184c2018-10-07 20:48:39 +02005113 // We don't want a 'q' typed at the more-prompt
5114 // interrupt loading a file.
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005115 got_int = FALSE;
Bram Moolenaar798184c2018-10-07 20:48:39 +02005116
5117 // If vimrc has "simalt ~x" we don't want it to
5118 // interfere with the prompt here.
Bram Moolenaar6a2633b2018-10-07 23:16:36 +02005119 flush_buffers(FLUSH_TYPEAHEAD);
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005120 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005121
5122#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
Bram Moolenaar188639d2022-04-04 16:57:21 +01005123 if (swap_exists_action != SEA_NONE
5124 && choice == SEA_CHOICE_NONE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005125 {
5126 char_u *name;
Bram Moolenaar188639d2022-04-04 16:57:21 +01005127 int dialog_result;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005128
Bram Moolenaar964b3742019-05-24 18:54:09 +02005129 name = alloc(STRLEN(fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005130 + STRLEN(_("Swap file \""))
Bram Moolenaar964b3742019-05-24 18:54:09 +02005131 + STRLEN(_("\" already exists!")) + 5);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005132 if (name != NULL)
5133 {
5134 STRCPY(name, _("Swap file \""));
5135 home_replace(NULL, fname, name + STRLEN(name),
5136 1000, TRUE);
5137 STRCAT(name, _("\" already exists!"));
5138 }
Bram Moolenaar188639d2022-04-04 16:57:21 +01005139 dialog_result = do_dialog(VIM_WARNING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005140 (char_u *)_("VIM - ATTENTION"),
5141 name == NULL
5142 ? (char_u *)_("Swap file already exists!")
5143 : name,
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02005144# ifdef HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar071d4272004-06-13 20:20:40 +00005145 process_still_running
5146 ? (char_u *)_("&Open Read-Only\n&Edit anyway\n&Recover\n&Quit\n&Abort") :
5147# endif
Bram Moolenaard2c340a2011-01-17 20:08:11 +01005148 (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 +00005149
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02005150# ifdef HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar188639d2022-04-04 16:57:21 +01005151 if (process_still_running && dialog_result >= 4)
5152 // compensate for missing "Delete it" button
5153 dialog_result++;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005154# endif
Bram Moolenaar188639d2022-04-04 16:57:21 +01005155 choice = dialog_result;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005156 vim_free(name);
5157
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005158 // pretend screen didn't scroll, need redraw anyway
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005159 msg_scrolled = 0;
Bram Moolenaara4d158b2022-08-14 14:17:45 +01005160 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005161 }
5162#endif
5163
Bram Moolenaar188639d2022-04-04 16:57:21 +01005164 switch (choice)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005165 {
Bram Moolenaar188639d2022-04-04 16:57:21 +01005166 case SEA_CHOICE_READONLY:
5167 buf->b_p_ro = TRUE;
5168 break;
5169 case SEA_CHOICE_EDIT:
5170 break;
5171 case SEA_CHOICE_RECOVER:
5172 swap_exists_action = SEA_RECOVER;
5173 break;
5174 case SEA_CHOICE_DELETE:
5175 mch_remove(fname);
5176 break;
5177 case SEA_CHOICE_QUIT:
5178 swap_exists_action = SEA_QUIT;
5179 break;
5180 case SEA_CHOICE_ABORT:
5181 swap_exists_action = SEA_QUIT;
5182 got_int = TRUE;
5183 break;
5184 case SEA_CHOICE_NONE:
5185 msg_puts("\n");
5186 if (msg_silent == 0)
5187 // call wait_return() later
5188 need_wait_return = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005189 break;
5190 }
Bram Moolenaar188639d2022-04-04 16:57:21 +01005191
5192 // If the file was deleted this fname can be used.
5193 if (choice != SEA_CHOICE_NONE && mch_getperm(fname) < 0)
5194 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005195
5196#ifdef CREATE_DUMMY_FILE
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005197 // Going to try another name, need the dummy file again.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005198 if (did_use_dummy)
Bram Moolenaar69c35002013-11-04 02:54:12 +01005199 dummyfd = mch_fopen((char *)buf_fname, "w");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005200#endif
5201 }
5202 }
5203 }
5204
5205 /*
5206 * Change the ".swp" extension to find another file that can be used.
5207 * First decrement the last char: ".swo", ".swn", etc.
5208 * If that still isn't enough decrement the last but one char: ".svz"
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00005209 * Can happen when editing many "No Name" buffers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005210 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005211 if (fname[n - 1] == 'a') // ".s?a"
Bram Moolenaar071d4272004-06-13 20:20:40 +00005212 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005213 if (fname[n - 2] == 'a') // ".saa": tried enough, give up
Bram Moolenaar071d4272004-06-13 20:20:40 +00005214 {
Bram Moolenaareaaac012022-01-02 17:00:40 +00005215 emsg(_(e_too_many_swap_files_found));
Bram Moolenaard23a8232018-02-10 18:45:26 +01005216 VIM_CLEAR(fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005217 break;
5218 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005219 --fname[n - 2]; // ".svz", ".suz", etc.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005220 fname[n - 1] = 'z' + 1;
5221 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005222 --fname[n - 1]; // ".swo", ".swn", etc.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005223 }
5224
5225 vim_free(dir_name);
5226#ifdef CREATE_DUMMY_FILE
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005227 if (dummyfd != NULL) // file has been created temporarily
Bram Moolenaar071d4272004-06-13 20:20:40 +00005228 {
5229 fclose(dummyfd);
Bram Moolenaar69c35002013-11-04 02:54:12 +01005230 mch_remove(buf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005231 }
5232#endif
Bram Moolenaar4f974752019-02-17 17:44:42 +01005233#ifdef MSWIN
Bram Moolenaar69c35002013-11-04 02:54:12 +01005234 if (buf_fname != buf->b_fname)
5235 vim_free(buf_fname);
5236#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005237 return fname;
5238}
5239
5240 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005241b0_magic_wrong(ZERO_BL *b0p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005242{
5243 return (b0p->b0_magic_long != (long)B0_MAGIC_LONG
5244 || b0p->b0_magic_int != (int)B0_MAGIC_INT
5245 || b0p->b0_magic_short != (short)B0_MAGIC_SHORT
5246 || b0p->b0_magic_char != B0_MAGIC_CHAR);
5247}
5248
5249#ifdef CHECK_INODE
5250/*
5251 * Compare current file name with file name from swap file.
5252 * Try to use inode numbers when possible.
5253 * Return non-zero when files are different.
5254 *
5255 * When comparing file names a few things have to be taken into consideration:
5256 * - When working over a network the full path of a file depends on the host.
5257 * We check the inode number if possible. It is not 100% reliable though,
5258 * because the device number cannot be used over a network.
5259 * - When a file does not exist yet (editing a new file) there is no inode
5260 * number.
5261 * - The file name in a swap file may not be valid on the current host. The
5262 * "~user" form is used whenever possible to avoid this.
5263 *
5264 * This is getting complicated, let's make a table:
5265 *
5266 * ino_c ino_s fname_c fname_s differ =
5267 *
5268 * both files exist -> compare inode numbers:
5269 * != 0 != 0 X X ino_c != ino_s
5270 *
5271 * inode number(s) unknown, file names available -> compare file names
5272 * == 0 X OK OK fname_c != fname_s
5273 * X == 0 OK OK fname_c != fname_s
5274 *
5275 * current file doesn't exist, file for swap file exist, file name(s) not
5276 * available -> probably different
5277 * == 0 != 0 FAIL X TRUE
5278 * == 0 != 0 X FAIL TRUE
5279 *
5280 * current file exists, inode for swap unknown, file name(s) not
5281 * available -> probably different
5282 * != 0 == 0 FAIL X TRUE
5283 * != 0 == 0 X FAIL TRUE
5284 *
5285 * current file doesn't exist, inode for swap unknown, one file name not
5286 * available -> probably different
5287 * == 0 == 0 FAIL OK TRUE
5288 * == 0 == 0 OK FAIL TRUE
5289 *
5290 * current file doesn't exist, inode for swap unknown, both file names not
Bram Moolenaar8c3169c2018-05-12 17:04:12 +02005291 * available -> compare file names
5292 * == 0 == 0 FAIL FAIL fname_c != fname_s
Bram Moolenaar071d4272004-06-13 20:20:40 +00005293 *
5294 * Note that when the ino_t is 64 bits, only the last 32 will be used. This
5295 * can't be changed without making the block 0 incompatible with 32 bit
5296 * versions.
5297 */
5298
5299 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005300fnamecmp_ino(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005301 char_u *fname_c, // current file name
5302 char_u *fname_s, // file name from swap file
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005303 long ino_block0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005304{
Bram Moolenaar8767f522016-07-01 17:17:39 +02005305 stat_T st;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005306 ino_t ino_c = 0; // ino of current file
5307 ino_t ino_s; // ino of file from swap file
5308 char_u buf_c[MAXPATHL]; // full path of fname_c
5309 char_u buf_s[MAXPATHL]; // full path of fname_s
5310 int retval_c; // flag: buf_c valid
5311 int retval_s; // flag: buf_s valid
Bram Moolenaar071d4272004-06-13 20:20:40 +00005312
5313 if (mch_stat((char *)fname_c, &st) == 0)
5314 ino_c = (ino_t)st.st_ino;
5315
5316 /*
5317 * First we try to get the inode from the file name, because the inode in
5318 * the swap file may be outdated. If that fails (e.g. this path is not
5319 * valid on this machine), use the inode from block 0.
5320 */
5321 if (mch_stat((char *)fname_s, &st) == 0)
5322 ino_s = (ino_t)st.st_ino;
5323 else
5324 ino_s = (ino_t)ino_block0;
5325
5326 if (ino_c && ino_s)
5327 return (ino_c != ino_s);
5328
5329 /*
5330 * One of the inode numbers is unknown, try a forced vim_FullName() and
5331 * compare the file names.
5332 */
5333 retval_c = vim_FullName(fname_c, buf_c, MAXPATHL, TRUE);
5334 retval_s = vim_FullName(fname_s, buf_s, MAXPATHL, TRUE);
5335 if (retval_c == OK && retval_s == OK)
Bram Moolenaar8c3169c2018-05-12 17:04:12 +02005336 return STRCMP(buf_c, buf_s) != 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005337
5338 /*
5339 * Can't compare inodes or file names, guess that the files are different,
Bram Moolenaar8c3169c2018-05-12 17:04:12 +02005340 * unless both appear not to exist at all, then compare with the file name
5341 * in the swap file.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005342 */
5343 if (ino_s == 0 && ino_c == 0 && retval_c == FAIL && retval_s == FAIL)
Bram Moolenaar8c3169c2018-05-12 17:04:12 +02005344 return STRCMP(fname_c, fname_s) != 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005345 return TRUE;
5346}
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005347#endif // CHECK_INODE
Bram Moolenaar071d4272004-06-13 20:20:40 +00005348
5349/*
5350 * Move a long integer into a four byte character array.
5351 * Used for machine independency in block zero.
5352 */
5353 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005354long_to_char(long n, char_u *s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005355{
5356 s[0] = (char_u)(n & 0xff);
5357 n = (unsigned)n >> 8;
5358 s[1] = (char_u)(n & 0xff);
5359 n = (unsigned)n >> 8;
5360 s[2] = (char_u)(n & 0xff);
5361 n = (unsigned)n >> 8;
5362 s[3] = (char_u)(n & 0xff);
5363}
5364
5365 static long
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005366char_to_long(char_u *s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005367{
5368 long retval;
5369
5370 retval = s[3];
5371 retval <<= 8;
5372 retval |= s[2];
5373 retval <<= 8;
5374 retval |= s[1];
5375 retval <<= 8;
5376 retval |= s[0];
5377
5378 return retval;
5379}
5380
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005381/*
5382 * Set the flags in the first block of the swap file:
5383 * - file is modified or not: buf->b_changed
5384 * - 'fileformat'
5385 * - 'fileencoding'
5386 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005387 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005388ml_setflags(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005389{
5390 bhdr_T *hp;
5391 ZERO_BL *b0p;
5392
5393 if (!buf->b_ml.ml_mfp)
5394 return;
5395 for (hp = buf->b_ml.ml_mfp->mf_used_last; hp != NULL; hp = hp->bh_prev)
5396 {
5397 if (hp->bh_bnum == 0)
5398 {
5399 b0p = (ZERO_BL *)(hp->bh_data);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005400 b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0;
5401 b0p->b0_flags = (b0p->b0_flags & ~B0_FF_MASK)
5402 | (get_fileformat(buf) + 1);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005403 add_b0_fenc(b0p, buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005404 hp->bh_flags |= BH_DIRTY;
5405 mf_sync(buf->b_ml.ml_mfp, MFS_ZERO);
5406 break;
5407 }
5408 }
5409}
5410
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005411#if defined(FEAT_CRYPT) || defined(PROTO)
5412/*
5413 * If "data" points to a data block encrypt the text in it and return a copy
5414 * in allocated memory. Return NULL when out of memory.
5415 * Otherwise return "data".
5416 */
5417 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005418ml_encrypt_data(
5419 memfile_T *mfp,
5420 char_u *data,
Bram Moolenaar8767f522016-07-01 17:17:39 +02005421 off_T offset,
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005422 unsigned size)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005423{
5424 DATA_BL *dp = (DATA_BL *)data;
5425 char_u *head_end;
5426 char_u *text_start;
5427 char_u *new_data;
5428 int text_len;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005429 cryptstate_T *state;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005430
5431 if (dp->db_id != DATA_ID)
5432 return data;
5433
Bram Moolenaarbc563362015-06-09 18:35:25 +02005434 state = ml_crypt_prepare(mfp, offset, FALSE);
5435 if (state == NULL)
5436 return data;
5437
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005438 new_data = alloc(size);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005439 if (new_data == NULL)
5440 return NULL;
5441 head_end = (char_u *)(&dp->db_index[dp->db_line_count]);
5442 text_start = (char_u *)dp + dp->db_txt_start;
5443 text_len = size - dp->db_txt_start;
5444
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005445 // Copy the header and the text.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005446 mch_memmove(new_data, dp, head_end - (char_u *)dp);
5447
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005448 // Encrypt the text.
Christian Brabandtf573c6e2021-06-20 14:02:16 +02005449 crypt_encode(state, text_start, text_len, new_data + dp->db_txt_start,
5450 FALSE);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005451 crypt_free_state(state);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005452
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005453 // Clear the gap.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005454 if (head_end < text_start)
5455 vim_memset(new_data + (head_end - data), 0, text_start - head_end);
5456
5457 return new_data;
5458}
5459
5460/*
Bram Moolenaarbc563362015-06-09 18:35:25 +02005461 * Decrypt the text in "data" if it points to an encrypted data block.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005462 */
5463 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005464ml_decrypt_data(
5465 memfile_T *mfp,
5466 char_u *data,
Bram Moolenaar8767f522016-07-01 17:17:39 +02005467 off_T offset,
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005468 unsigned size)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005469{
5470 DATA_BL *dp = (DATA_BL *)data;
5471 char_u *head_end;
5472 char_u *text_start;
5473 int text_len;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005474 cryptstate_T *state;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005475
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00005476 if (dp->db_id != DATA_ID)
5477 return;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005478
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00005479 head_end = (char_u *)(&dp->db_index[dp->db_line_count]);
5480 text_start = (char_u *)dp + dp->db_txt_start;
5481 text_len = dp->db_txt_end - dp->db_txt_start;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005482
Yegappan Lakshmanane8575982023-01-14 12:32:28 +00005483 if (head_end > text_start || dp->db_txt_start > size
5484 || dp->db_txt_end > size)
5485 return; // data was messed up
5486
5487 state = ml_crypt_prepare(mfp, offset, TRUE);
5488 if (state == NULL)
5489 return;
5490
5491 // Decrypt the text in place.
5492 crypt_decode_inplace(state, text_start, text_len, FALSE);
5493 crypt_free_state(state);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005494}
5495
5496/*
5497 * Prepare for encryption/decryption, using the key, seed and offset.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005498 * Return an allocated cryptstate_T *.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005499 */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005500 static cryptstate_T *
Bram Moolenaar8767f522016-07-01 17:17:39 +02005501ml_crypt_prepare(memfile_T *mfp, off_T offset, int reading)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005502{
5503 buf_T *buf = mfp->mf_buffer;
5504 char_u salt[50];
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005505 int method_nr;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005506 char_u *key;
5507 char_u *seed;
5508
5509 if (reading && mfp->mf_old_key != NULL)
5510 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005511 // Reading back blocks with the previous key/method/seed.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005512 method_nr = mfp->mf_old_cm;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005513 key = mfp->mf_old_key;
5514 seed = mfp->mf_old_seed;
5515 }
5516 else
5517 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005518 method_nr = crypt_get_method_nr(buf);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005519 key = buf->b_p_key;
5520 seed = mfp->mf_seed;
5521 }
Bram Moolenaarbc563362015-06-09 18:35:25 +02005522 if (*key == NUL)
5523 return NULL;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005524
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005525 if (method_nr == CRYPT_M_ZIP)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005526 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005527 // For PKzip: Append the offset to the key, so that we use a different
5528 // key for every block.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005529 vim_snprintf((char *)salt, sizeof(salt), "%s%ld", key, (long)offset);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005530 return crypt_create(method_nr, salt, NULL, 0, NULL, 0);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005531 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005532
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005533 // Using blowfish or better: add salt and seed. We use the byte offset
5534 // of the block for the salt.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005535 vim_snprintf((char *)salt, sizeof(salt), "%ld", (long)offset);
5536 return crypt_create(method_nr, key, salt, (int)STRLEN(salt),
Christian Brabandtf573c6e2021-06-20 14:02:16 +02005537 seed, MF_SEED_LEN);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005538}
5539
5540#endif
5541
5542
Bram Moolenaar071d4272004-06-13 20:20:40 +00005543#if defined(FEAT_BYTEOFF) || defined(PROTO)
5544
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005545#define MLCS_MAXL 800 // max no of lines in chunk
5546#define MLCS_MINL 400 // should be half of MLCS_MAXL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005547
5548/*
Bram Moolenaar0ad014c2010-07-25 14:00:46 +02005549 * Keep information for finding byte offset of a line, updtype may be one of:
Bram Moolenaar071d4272004-06-13 20:20:40 +00005550 * ML_CHNK_ADDLINE: Add len to parent chunk, possibly splitting it
5551 * Careful: ML_CHNK_ADDLINE may cause ml_find_line() to be called.
5552 * ML_CHNK_DELLINE: Subtract len from parent chunk, possibly deleting it
5553 * ML_CHNK_UPDLINE: Add len to parent chunk, as a signed entity.
5554 */
5555 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005556ml_updatechunk(
5557 buf_T *buf,
5558 linenr_T line,
5559 long len,
5560 int updtype)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005561{
5562 static buf_T *ml_upd_lastbuf = NULL;
5563 static linenr_T ml_upd_lastline;
5564 static linenr_T ml_upd_lastcurline;
5565 static int ml_upd_lastcurix;
5566
5567 linenr_T curline = ml_upd_lastcurline;
5568 int curix = ml_upd_lastcurix;
5569 long size;
5570 chunksize_T *curchnk;
5571 int rest;
5572 bhdr_T *hp;
5573 DATA_BL *dp;
5574
5575 if (buf->b_ml.ml_usedchunks == -1 || len == 0)
5576 return;
5577 if (buf->b_ml.ml_chunksize == NULL)
5578 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005579 buf->b_ml.ml_chunksize = ALLOC_MULT(chunksize_T, 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005580 if (buf->b_ml.ml_chunksize == NULL)
5581 {
5582 buf->b_ml.ml_usedchunks = -1;
5583 return;
5584 }
5585 buf->b_ml.ml_numchunks = 100;
5586 buf->b_ml.ml_usedchunks = 1;
5587 buf->b_ml.ml_chunksize[0].mlcs_numlines = 1;
5588 buf->b_ml.ml_chunksize[0].mlcs_totalsize = 1;
5589 }
5590
5591 if (updtype == ML_CHNK_UPDLINE && buf->b_ml.ml_line_count == 1)
5592 {
5593 /*
5594 * First line in empty buffer from ml_flush_line() -- reset
5595 */
5596 buf->b_ml.ml_usedchunks = 1;
5597 buf->b_ml.ml_chunksize[0].mlcs_numlines = 1;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01005598 buf->b_ml.ml_chunksize[0].mlcs_totalsize = (long)buf->b_ml.ml_line_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005599 return;
5600 }
5601
5602 /*
5603 * Find chunk that our line belongs to, curline will be at start of the
5604 * chunk.
5605 */
5606 if (buf != ml_upd_lastbuf || line != ml_upd_lastline + 1
5607 || updtype != ML_CHNK_ADDLINE)
5608 {
5609 for (curline = 1, curix = 0;
5610 curix < buf->b_ml.ml_usedchunks - 1
5611 && line >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines;
5612 curix++)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005613 curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005614 }
Bram Moolenaara9a8e042018-10-30 22:15:55 +01005615 else if (curix < buf->b_ml.ml_usedchunks - 1
5616 && line >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005617 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005618 // Adjust cached curix & curline
Bram Moolenaar071d4272004-06-13 20:20:40 +00005619 curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
5620 curix++;
5621 }
5622 curchnk = buf->b_ml.ml_chunksize + curix;
5623
5624 if (updtype == ML_CHNK_DELLINE)
Bram Moolenaar5a6404c2006-11-01 17:12:57 +00005625 len = -len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005626 curchnk->mlcs_totalsize += len;
5627 if (updtype == ML_CHNK_ADDLINE)
5628 {
5629 curchnk->mlcs_numlines++;
5630
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005631 // May resize here so we don't have to do it in both cases below
Bram Moolenaar071d4272004-06-13 20:20:40 +00005632 if (buf->b_ml.ml_usedchunks + 1 >= buf->b_ml.ml_numchunks)
5633 {
Bram Moolenaar9abd5c62015-02-10 18:34:01 +01005634 chunksize_T *t_chunksize = buf->b_ml.ml_chunksize;
5635
Bram Moolenaar071d4272004-06-13 20:20:40 +00005636 buf->b_ml.ml_numchunks = buf->b_ml.ml_numchunks * 3 / 2;
Bram Moolenaar51b6eb42020-08-22 15:19:18 +02005637 buf->b_ml.ml_chunksize = vim_realloc(buf->b_ml.ml_chunksize,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005638 sizeof(chunksize_T) * buf->b_ml.ml_numchunks);
5639 if (buf->b_ml.ml_chunksize == NULL)
5640 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005641 // Hmmmm, Give up on offset for this buffer
Bram Moolenaar9abd5c62015-02-10 18:34:01 +01005642 vim_free(t_chunksize);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005643 buf->b_ml.ml_usedchunks = -1;
5644 return;
5645 }
5646 }
5647
5648 if (buf->b_ml.ml_chunksize[curix].mlcs_numlines >= MLCS_MAXL)
5649 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005650 int count; // number of entries in block
Bram Moolenaar071d4272004-06-13 20:20:40 +00005651 int idx;
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005652 int end_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005653 int text_end;
5654 int linecnt;
5655
5656 mch_memmove(buf->b_ml.ml_chunksize + curix + 1,
5657 buf->b_ml.ml_chunksize + curix,
5658 (buf->b_ml.ml_usedchunks - curix) *
5659 sizeof(chunksize_T));
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005660 // Compute length of first half of lines in the split chunk
Bram Moolenaar071d4272004-06-13 20:20:40 +00005661 size = 0;
5662 linecnt = 0;
5663 while (curline < buf->b_ml.ml_line_count
5664 && linecnt < MLCS_MINL)
5665 {
5666 if ((hp = ml_find_line(buf, curline, ML_FIND)) == NULL)
5667 {
5668 buf->b_ml.ml_usedchunks = -1;
5669 return;
5670 }
5671 dp = (DATA_BL *)(hp->bh_data);
5672 count = (long)(buf->b_ml.ml_locked_high) -
5673 (long)(buf->b_ml.ml_locked_low) + 1;
5674 idx = curline - buf->b_ml.ml_locked_low;
5675 curline = buf->b_ml.ml_locked_high + 1;
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005676
5677 // compute index of last line to use in this MEMLINE
Bram Moolenaar071d4272004-06-13 20:20:40 +00005678 rest = count - idx;
5679 if (linecnt + rest > MLCS_MINL)
5680 {
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005681 end_idx = idx + MLCS_MINL - linecnt - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005682 linecnt = MLCS_MINL;
5683 }
5684 else
5685 {
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005686 end_idx = count - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005687 linecnt += rest;
5688 }
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01005689#ifdef FEAT_PROP_POPUP
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005690 if (buf->b_has_textprop)
5691 {
5692 int i;
5693
5694 // We cannot use the text pointers to get the text length,
5695 // the text prop info would also be counted. Go over the
5696 // lines.
5697 for (i = end_idx; i < idx; ++i)
Bram Moolenaare5a0e8c2022-08-09 21:37:55 +01005698 size += (int)STRLEN((char_u *)dp
5699 + (dp->db_index[i] & DB_INDEX_MASK)) + 1;
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005700 }
5701 else
5702#endif
5703 {
Bram Moolenaar14c75302021-08-15 14:28:40 +02005704 if (idx == 0) // first line in block, text at the end
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005705 text_end = dp->db_txt_end;
5706 else
5707 text_end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK);
Bram Moolenaare5a0e8c2022-08-09 21:37:55 +01005708 size += text_end
5709 - ((dp->db_index[end_idx]) & DB_INDEX_MASK);
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005710 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005711 }
5712 buf->b_ml.ml_chunksize[curix].mlcs_numlines = linecnt;
5713 buf->b_ml.ml_chunksize[curix + 1].mlcs_numlines -= linecnt;
5714 buf->b_ml.ml_chunksize[curix].mlcs_totalsize = size;
5715 buf->b_ml.ml_chunksize[curix + 1].mlcs_totalsize -= size;
5716 buf->b_ml.ml_usedchunks++;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005717 ml_upd_lastbuf = NULL; // Force recalc of curix & curline
Bram Moolenaar071d4272004-06-13 20:20:40 +00005718 return;
5719 }
5720 else if (buf->b_ml.ml_chunksize[curix].mlcs_numlines >= MLCS_MINL
5721 && curix == buf->b_ml.ml_usedchunks - 1
5722 && buf->b_ml.ml_line_count - line <= 1)
5723 {
5724 /*
Bram Moolenaar8e7d6222020-12-18 19:49:56 +01005725 * We are in the last chunk and it is cheap to create a new one
Bram Moolenaar071d4272004-06-13 20:20:40 +00005726 * after this. Do it now to avoid the loop above later on
5727 */
5728 curchnk = buf->b_ml.ml_chunksize + curix + 1;
5729 buf->b_ml.ml_usedchunks++;
5730 if (line == buf->b_ml.ml_line_count)
5731 {
5732 curchnk->mlcs_numlines = 0;
5733 curchnk->mlcs_totalsize = 0;
5734 }
5735 else
5736 {
5737 /*
5738 * Line is just prior to last, move count for last
5739 * This is the common case when loading a new file
5740 */
5741 hp = ml_find_line(buf, buf->b_ml.ml_line_count, ML_FIND);
5742 if (hp == NULL)
5743 {
5744 buf->b_ml.ml_usedchunks = -1;
5745 return;
5746 }
5747 dp = (DATA_BL *)(hp->bh_data);
5748 if (dp->db_line_count == 1)
5749 rest = dp->db_txt_end - dp->db_txt_start;
5750 else
5751 rest =
5752 ((dp->db_index[dp->db_line_count - 2]) & DB_INDEX_MASK)
5753 - dp->db_txt_start;
5754 curchnk->mlcs_totalsize = rest;
5755 curchnk->mlcs_numlines = 1;
5756 curchnk[-1].mlcs_totalsize -= rest;
5757 curchnk[-1].mlcs_numlines -= 1;
5758 }
5759 }
5760 }
5761 else if (updtype == ML_CHNK_DELLINE)
5762 {
5763 curchnk->mlcs_numlines--;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005764 ml_upd_lastbuf = NULL; // Force recalc of curix & curline
Bram Moolenaare5a0e8c2022-08-09 21:37:55 +01005765 if (curix < buf->b_ml.ml_usedchunks - 1
5766 && curchnk->mlcs_numlines + curchnk[1].mlcs_numlines
5767 <= MLCS_MINL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005768 {
5769 curix++;
5770 curchnk = buf->b_ml.ml_chunksize + curix;
5771 }
5772 else if (curix == 0 && curchnk->mlcs_numlines <= 0)
5773 {
5774 buf->b_ml.ml_usedchunks--;
5775 mch_memmove(buf->b_ml.ml_chunksize, buf->b_ml.ml_chunksize + 1,
5776 buf->b_ml.ml_usedchunks * sizeof(chunksize_T));
5777 return;
5778 }
5779 else if (curix == 0 || (curchnk->mlcs_numlines > 10
Bram Moolenaare5a0e8c2022-08-09 21:37:55 +01005780 && curchnk->mlcs_numlines + curchnk[-1].mlcs_numlines
5781 > MLCS_MINL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005782 {
5783 return;
5784 }
5785
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005786 // Collapse chunks
Bram Moolenaar071d4272004-06-13 20:20:40 +00005787 curchnk[-1].mlcs_numlines += curchnk->mlcs_numlines;
5788 curchnk[-1].mlcs_totalsize += curchnk->mlcs_totalsize;
5789 buf->b_ml.ml_usedchunks--;
5790 if (curix < buf->b_ml.ml_usedchunks)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005791 mch_memmove(buf->b_ml.ml_chunksize + curix,
5792 buf->b_ml.ml_chunksize + curix + 1,
5793 (buf->b_ml.ml_usedchunks - curix) *
5794 sizeof(chunksize_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005795 return;
5796 }
5797 ml_upd_lastbuf = buf;
5798 ml_upd_lastline = line;
5799 ml_upd_lastcurline = curline;
5800 ml_upd_lastcurix = curix;
5801}
5802
5803/*
5804 * Find offset for line or line with offset.
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005805 * Find line with offset if "lnum" is 0; return remaining offset in offp
5806 * Find offset of line if "lnum" > 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00005807 * return -1 if information is not available
5808 */
5809 long
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005810ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005811{
5812 linenr_T curline;
5813 int curix;
5814 long size;
5815 bhdr_T *hp;
5816 DATA_BL *dp;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005817 int count; // number of entries in block
Bram Moolenaar071d4272004-06-13 20:20:40 +00005818 int idx;
5819 int start_idx;
5820 int text_end;
5821 long offset;
5822 int len;
5823 int ffdos = (get_fileformat(buf) == EOL_DOS);
5824 int extra = 0;
5825
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005826 // take care of cached line first
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005827 ml_flush_line(curbuf);
5828
Bram Moolenaar071d4272004-06-13 20:20:40 +00005829 if (buf->b_ml.ml_usedchunks == -1
5830 || buf->b_ml.ml_chunksize == NULL
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005831 || lnum < 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005832 return -1;
5833
5834 if (offp == NULL)
5835 offset = 0;
5836 else
5837 offset = *offp;
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005838 if (lnum == 0 && offset <= 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005839 return 1; // Not a "find offset" and offset 0 _must_ be in line 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00005840 /*
5841 * Find the last chunk before the one containing our line. Last chunk is
Bram Moolenaar14c75302021-08-15 14:28:40 +02005842 * special because it will never qualify.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005843 */
5844 curline = 1;
5845 curix = size = 0;
5846 while (curix < buf->b_ml.ml_usedchunks - 1
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005847 && ((lnum != 0
5848 && lnum >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005849 || (offset != 0
5850 && offset > size + buf->b_ml.ml_chunksize[curix].mlcs_totalsize
=?UTF-8?q?Dundar=20G=C3=B6c?=d5cec1f2022-01-29 15:19:23 +00005851 + (long)ffdos * buf->b_ml.ml_chunksize[curix].mlcs_numlines)))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005852 {
5853 curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
5854 size += buf->b_ml.ml_chunksize[curix].mlcs_totalsize;
5855 if (offset && ffdos)
5856 size += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
5857 curix++;
5858 }
5859
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005860 while ((lnum != 0 && curline < lnum) || (offset != 0 && size < offset))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005861 {
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005862#ifdef FEAT_PROP_POPUP
5863 size_t textprop_total = 0;
5864#endif
5865
Bram Moolenaar071d4272004-06-13 20:20:40 +00005866 if (curline > buf->b_ml.ml_line_count
5867 || (hp = ml_find_line(buf, curline, ML_FIND)) == NULL)
5868 return -1;
5869 dp = (DATA_BL *)(hp->bh_data);
5870 count = (long)(buf->b_ml.ml_locked_high) -
5871 (long)(buf->b_ml.ml_locked_low) + 1;
5872 start_idx = idx = curline - buf->b_ml.ml_locked_low;
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005873 if (idx == 0) // first line in block, text at the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00005874 text_end = dp->db_txt_end;
5875 else
5876 text_end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005877 // Compute index of last line to use in this MEMLINE
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005878 if (lnum != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005879 {
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005880 if (curline + (count - idx) >= lnum)
5881 idx += lnum - curline - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005882 else
5883 idx = count - 1;
5884 }
5885 else
5886 {
5887 extra = 0;
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005888 for (;;)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005889 {
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005890#ifdef FEAT_PROP_POPUP
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005891 size_t textprop_size = 0;
5892
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005893 if (buf->b_has_textprop)
5894 {
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005895 char_u *l1, *l2;
5896
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005897 // compensate for the extra bytes taken by textprops
5898 l1 = (char_u *)dp + ((dp->db_index[idx]) & DB_INDEX_MASK);
5899 l2 = (char_u *)dp + (idx == 0 ? dp->db_txt_end
5900 : ((dp->db_index[idx - 1]) & DB_INDEX_MASK));
5901 textprop_size = (l2 - l1) - (STRLEN(l1) + 1);
5902 }
5903#endif
5904 if (!(offset >= size
5905 + text_end - (int)((dp->db_index[idx]) & DB_INDEX_MASK)
5906#ifdef FEAT_PROP_POPUP
Bram Moolenaar94b6fb72020-01-17 21:00:59 +01005907 - (long)(textprop_total + textprop_size)
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005908#endif
5909 + ffdos))
5910 break;
5911
Bram Moolenaar071d4272004-06-13 20:20:40 +00005912 if (ffdos)
5913 size++;
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005914#ifdef FEAT_PROP_POPUP
5915 textprop_total += textprop_size;
5916#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005917 if (idx == count - 1)
5918 {
5919 extra = 1;
5920 break;
5921 }
5922 idx++;
5923 }
5924 }
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01005925#ifdef FEAT_PROP_POPUP
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005926 if (buf->b_has_textprop && lnum != 0)
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005927 {
5928 int i;
5929
5930 // cannot use the db_index pointer, need to get the actual text
5931 // lengths.
5932 len = 0;
5933 for (i = start_idx; i <= idx; ++i)
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005934 {
5935 char_u *p = (char_u *)dp + ((dp->db_index[i]) & DB_INDEX_MASK);
5936 len += (int)STRLEN(p) + 1;
5937 }
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005938 }
5939 else
5940#endif
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005941 len = text_end - ((dp->db_index[idx]) & DB_INDEX_MASK)
5942#ifdef FEAT_PROP_POPUP
5943 - (long)textprop_total
5944#endif
5945 ;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005946 size += len;
5947 if (offset != 0 && size >= offset)
5948 {
5949 if (size + ffdos == offset)
5950 *offp = 0;
5951 else if (idx == start_idx)
5952 *offp = offset - size + len;
5953 else
5954 *offp = offset - size + len
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005955 - (text_end - ((dp->db_index[idx - 1]) & DB_INDEX_MASK))
5956#ifdef FEAT_PROP_POPUP
5957 + (long)textprop_total
5958#endif
5959 ;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005960 curline += idx - start_idx + extra;
5961 if (curline > buf->b_ml.ml_line_count)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005962 return -1; // exactly one byte beyond the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00005963 return curline;
5964 }
5965 curline = buf->b_ml.ml_locked_high + 1;
5966 }
5967
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005968 if (lnum != 0)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005969 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005970 // Count extra CR characters.
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005971 if (ffdos)
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005972 size += lnum - 1;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005973
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005974 // Don't count the last line break if 'noeol' and ('bin' or
5975 // 'nofixeol').
Bram Moolenaar34d72d42015-07-17 14:18:08 +02005976 if ((!buf->b_p_fixeol || buf->b_p_bin) && !buf->b_p_eol
Bram Moolenaarc26f7c62018-08-20 22:53:04 +02005977 && lnum > buf->b_ml.ml_line_count)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005978 size -= ffdos + 1;
5979 }
5980
Bram Moolenaar071d4272004-06-13 20:20:40 +00005981 return size;
5982}
5983
5984/*
5985 * Goto byte in buffer with offset 'cnt'.
5986 */
5987 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005988goto_byte(long cnt)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005989{
5990 long boff = cnt;
5991 linenr_T lnum;
5992
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005993 ml_flush_line(curbuf); // cached line may be dirty
Bram Moolenaar071d4272004-06-13 20:20:40 +00005994 setpcmark();
5995 if (boff)
5996 --boff;
5997 lnum = ml_find_line_or_offset(curbuf, (linenr_T)0, &boff);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005998 if (lnum < 1) // past the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00005999 {
6000 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
6001 curwin->w_curswant = MAXCOL;
6002 coladvance((colnr_T)MAXCOL);
6003 }
6004 else
6005 {
6006 curwin->w_cursor.lnum = lnum;
6007 curwin->w_cursor.col = (colnr_T)boff;
Bram Moolenaar943d2b52005-12-02 00:50:49 +00006008 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006009 curwin->w_set_curswant = TRUE;
6010 }
6011 check_cursor();
6012
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01006013 // Make sure the cursor is on the first byte of a multi-byte char.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006014 if (has_mbyte)
6015 mb_adjust_cursor();
Bram Moolenaar071d4272004-06-13 20:20:40 +00006016}
6017#endif