blob: f1c2a8a524452014f6313651f185ff929c3ab151 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010010// for debugging
11// #define CHECK(c, s) do { if (c) emsg((s)); } while (0)
Bram Moolenaar6f470022018-04-10 18:47:20 +020012#define CHECK(c, s) do { /**/ } while (0)
Bram Moolenaar071d4272004-06-13 20:20:40 +000013
14/*
15 * memline.c: Contains the functions for appending, deleting and changing the
Bram Moolenaar4770d092006-01-12 23:22:24 +000016 * text lines. The memfile functions are used to store the information in
17 * blocks of memory, backed up by a file. The structure of the information is
18 * a tree. The root of the tree is a pointer block. The leaves of the tree
19 * are data blocks. In between may be several layers of pointer blocks,
20 * forming branches.
Bram Moolenaar071d4272004-06-13 20:20:40 +000021 *
22 * Three types of blocks are used:
23 * - Block nr 0 contains information for recovery
24 * - Pointer blocks contain list of pointers to other blocks.
25 * - Data blocks contain the actual text.
26 *
27 * Block nr 0 contains the block0 structure (see below).
28 *
29 * Block nr 1 is the first pointer block. It is the root of the tree.
30 * Other pointer blocks are branches.
31 *
32 * If a line is too big to fit in a single page, the block containing that
33 * line is made big enough to hold the line. It may span several pages.
34 * Otherwise all blocks are one page.
35 *
36 * A data block that was filled when starting to edit a file and was not
37 * changed since then, can have a negative block number. This means that it
38 * has not yet been assigned a place in the file. When recovering, the lines
39 * in this data block can be read from the original file. When the block is
40 * changed (lines appended/deleted/changed) or when it is flushed it gets a
41 * positive number. Use mf_trans_del() to get the new number, before calling
42 * mf_get().
43 */
44
Bram Moolenaar071d4272004-06-13 20:20:40 +000045#include "vim.h"
46
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010047#ifndef UNIX // it's in os_unix.h for Unix
Bram Moolenaar071d4272004-06-13 20:20:40 +000048# include <time.h>
49#endif
50
Christian Brabandtf573c6e2021-06-20 14:02:16 +020051// for randombytes_buf
52#ifdef FEAT_SODIUM
53# include <sodium.h>
54#endif
55
Bram Moolenaar5a6404c2006-11-01 17:12:57 +000056#if defined(SASC) || defined(__amigaos4__)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010057# include <proto/dos.h> // for Open() and Close()
Bram Moolenaar071d4272004-06-13 20:20:40 +000058#endif
59
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010060typedef struct block0 ZERO_BL; // contents of the first block
61typedef struct pointer_block PTR_BL; // contents of a pointer block
62typedef struct data_block DATA_BL; // contents of a data block
63typedef struct pointer_entry PTR_EN; // block/line-count pair
Bram Moolenaar071d4272004-06-13 20:20:40 +000064
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010065#define DATA_ID (('d' << 8) + 'a') // data block id
66#define PTR_ID (('p' << 8) + 't') // pointer block id
67#define BLOCK0_ID0 'b' // block 0 id 0
68#define BLOCK0_ID1 '0' // block 0 id 1
69#define BLOCK0_ID1_C0 'c' // block 0 id 1 'cm' 0
70#define BLOCK0_ID1_C1 'C' // block 0 id 1 'cm' 1
71#define BLOCK0_ID1_C2 'd' // block 0 id 1 'cm' 2
Christian Brabandtf573c6e2021-06-20 14:02:16 +020072#define BLOCK0_ID1_C3 'S' // block 0 id 1 'cm' 3 - but not actually used
Bram Moolenaar8f4ac012014-08-10 13:38:34 +020073
74#if defined(FEAT_CRYPT)
75static int id1_codes[] = {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010076 BLOCK0_ID1_C0, // CRYPT_M_ZIP
77 BLOCK0_ID1_C1, // CRYPT_M_BF
78 BLOCK0_ID1_C2, // CRYPT_M_BF2
Christian Brabandtf573c6e2021-06-20 14:02:16 +020079 BLOCK0_ID1_C3, // CRYPT_M_SOD - Unused!
Bram Moolenaar8f4ac012014-08-10 13:38:34 +020080};
81#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000082
83/*
84 * pointer to a block, used in a pointer block
85 */
86struct pointer_entry
87{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010088 blocknr_T pe_bnum; // block number
89 linenr_T pe_line_count; // number of lines in this branch
90 linenr_T pe_old_lnum; // lnum for this block (for recovery)
91 int pe_page_count; // number of pages in block pe_bnum
Bram Moolenaar071d4272004-06-13 20:20:40 +000092};
93
94/*
95 * A pointer block contains a list of branches in the tree.
96 */
97struct pointer_block
98{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010099 short_u pb_id; // ID for pointer block: PTR_ID
100 short_u pb_count; // number of pointers in this block
101 short_u pb_count_max; // maximum value for pb_count
102 PTR_EN pb_pointer[1]; // list of pointers to blocks (actually longer)
103 // followed by empty space until end of page
Bram Moolenaar071d4272004-06-13 20:20:40 +0000104};
105
106/*
107 * A data block is a leaf in the tree.
108 *
109 * The text of the lines is at the end of the block. The text of the first line
110 * in the block is put at the end, the text of the second line in front of it,
111 * etc. Thus the order of the lines is the opposite of the line number.
112 */
113struct data_block
114{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100115 short_u db_id; // ID for data block: DATA_ID
116 unsigned db_free; // free space available
117 unsigned db_txt_start; // byte where text starts
118 unsigned db_txt_end; // byte just after data block
119 linenr_T db_line_count; // number of lines in this block
120 unsigned db_index[1]; // index for start of line (actually bigger)
Bram Moolenaar4b96df52020-01-26 22:00:26 +0100121 // followed by empty space up to db_txt_start
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100122 // followed by the text in the lines until
123 // end of page
Bram Moolenaar071d4272004-06-13 20:20:40 +0000124};
125
126/*
127 * The low bits of db_index hold the actual index. The topmost bit is
128 * used for the global command to be able to mark a line.
129 * This method is not clean, but otherwise there would be at least one extra
130 * byte used for each line.
131 * The mark has to be in this place to keep it with the correct line when other
132 * lines are inserted or deleted.
133 */
134#define DB_MARKED ((unsigned)1 << ((sizeof(unsigned) * 8) - 1))
135#define DB_INDEX_MASK (~DB_MARKED)
136
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100137#define INDEX_SIZE (sizeof(unsigned)) // size of one db_index entry
138#define HEADER_SIZE (sizeof(DATA_BL) - INDEX_SIZE) // size of data block header
Bram Moolenaar071d4272004-06-13 20:20:40 +0000139
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100140#define B0_FNAME_SIZE_ORG 900 // what it was in older versions
141#define B0_FNAME_SIZE_NOCRYPT 898 // 2 bytes used for other things
142#define B0_FNAME_SIZE_CRYPT 890 // 10 bytes used for other things
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000143#define B0_UNAME_SIZE 40
144#define B0_HNAME_SIZE 40
Bram Moolenaar071d4272004-06-13 20:20:40 +0000145/*
146 * Restrict the numbers to 32 bits, otherwise most compilers will complain.
147 * This won't detect a 64 bit machine that only swaps a byte in the top 32
148 * bits, but that is crazy anyway.
149 */
150#define B0_MAGIC_LONG 0x30313233L
151#define B0_MAGIC_INT 0x20212223L
152#define B0_MAGIC_SHORT 0x10111213L
153#define B0_MAGIC_CHAR 0x55
154
155/*
156 * Block zero holds all info about the swap file.
157 *
158 * NOTE: DEFINITION OF BLOCK 0 SHOULD NOT CHANGE! It would make all existing
159 * swap files unusable!
160 *
161 * If size of block0 changes anyway, adjust MIN_SWAP_PAGE_SIZE in vim.h!!
162 *
Bram Moolenaarbae0c162007-05-10 19:30:25 +0000163 * This block is built up of single bytes, to make it portable across
Bram Moolenaar071d4272004-06-13 20:20:40 +0000164 * different machines. b0_magic_* is used to check the byte order and size of
165 * variables, because the rest of the swap file is not portable.
166 */
167struct block0
168{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100169 char_u b0_id[2]; // id for block 0: BLOCK0_ID0 and BLOCK0_ID1,
170 // BLOCK0_ID1_C0, BLOCK0_ID1_C1, etc.
171 char_u b0_version[10]; // Vim version string
172 char_u b0_page_size[4];// number of bytes per page
173 char_u b0_mtime[4]; // last modification time of file
174 char_u b0_ino[4]; // inode of b0_fname
175 char_u b0_pid[4]; // process id of creator (or 0)
176 char_u b0_uname[B0_UNAME_SIZE]; // name of user (uid if no name)
177 char_u b0_hname[B0_HNAME_SIZE]; // host name (if it has a name)
178 char_u b0_fname[B0_FNAME_SIZE_ORG]; // name of file being edited
179 long b0_magic_long; // check for byte order of long
180 int b0_magic_int; // check for byte order of int
181 short b0_magic_short; // check for byte order of short
182 char_u b0_magic_char; // check for last char
Bram Moolenaar071d4272004-06-13 20:20:40 +0000183};
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000184
185/*
Bram Moolenaar4770d092006-01-12 23:22:24 +0000186 * Note: b0_dirty and b0_flags are put at the end of the file name. For very
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000187 * long file names in older versions of Vim they are invalid.
188 * The 'fileencoding' comes before b0_flags, with a NUL in front. But only
189 * when there is room, for very long file names it's omitted.
190 */
191#define B0_DIRTY 0x55
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200192#define b0_dirty b0_fname[B0_FNAME_SIZE_ORG - 1]
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000193
194/*
195 * The b0_flags field is new in Vim 7.0.
196 */
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200197#define b0_flags b0_fname[B0_FNAME_SIZE_ORG - 2]
198
199/*
200 * Crypt seed goes here, 8 bytes. New in Vim 7.3.
201 * Without encryption these bytes may be used for 'fenc'.
202 */
203#define b0_seed b0_fname[B0_FNAME_SIZE_ORG - 2 - MF_SEED_LEN]
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000204
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100205// The lowest two bits contain the fileformat. Zero means it's not set
206// (compatible with Vim 6.x), otherwise it's EOL_UNIX + 1, EOL_DOS + 1 or
207// EOL_MAC + 1.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000208#define B0_FF_MASK 3
209
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100210// Swap file is in directory of edited file. Used to find the file from
211// different mount points.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000212#define B0_SAME_DIR 4
213
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100214// The 'fileencoding' is at the end of b0_fname[], with a NUL in front of it.
215// When empty there is only the NUL.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000216#define B0_HAS_FENC 8
Bram Moolenaar071d4272004-06-13 20:20:40 +0000217
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100218#define STACK_INCR 5 // nr of entries added to ml_stack at a time
Bram Moolenaar071d4272004-06-13 20:20:40 +0000219
220/*
221 * The line number where the first mark may be is remembered.
222 * If it is 0 there are no marks at all.
223 * (always used for the current buffer only, no buffer change possible while
224 * executing a global command).
225 */
226static linenr_T lowest_marked = 0;
227
228/*
229 * arguments for ml_find_line()
230 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100231#define ML_DELETE 0x11 // delete line
232#define ML_INSERT 0x12 // insert line
233#define ML_FIND 0x13 // just find the line
234#define ML_FLUSH 0x02 // flush locked block
235#define ML_SIMPLE(x) (x & 0x10) // DEL, INS or FIND
Bram Moolenaar071d4272004-06-13 20:20:40 +0000236
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100237// argument for ml_upd_block0()
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200238typedef enum {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100239 UB_FNAME = 0 // update timestamp and filename
240 , UB_SAME_DIR // update the B0_SAME_DIR flag
241 , UB_CRYPT // update crypt key
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200242} upd_block0_T;
243
244#ifdef FEAT_CRYPT
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100245static void ml_set_b0_crypt(buf_T *buf, ZERO_BL *b0p);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200246#endif
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100247static void ml_upd_block0(buf_T *buf, upd_block0_T what);
248static void set_b0_fname(ZERO_BL *, buf_T *buf);
249static void set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100250static void add_b0_fenc(ZERO_BL *b0p, buf_T *buf);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100251static time_t swapfile_info(char_u *);
252static int recov_file_names(char_u **, char_u *, int prepend_dot);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100253static char_u *findswapname(buf_T *, char_u **, char_u *);
254static void ml_flush_line(buf_T *);
255static bhdr_T *ml_new_data(memfile_T *, int, int);
256static bhdr_T *ml_new_ptr(memfile_T *);
257static bhdr_T *ml_find_line(buf_T *, linenr_T, int);
258static int ml_add_stack(buf_T *);
259static void ml_lineadd(buf_T *, int);
260static int b0_magic_wrong(ZERO_BL *);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000261#ifdef CHECK_INODE
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100262static int fnamecmp_ino(char_u *, char_u *, long);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000263#endif
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100264static void long_to_char(long, char_u *);
265static long char_to_long(char_u *);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200266#ifdef FEAT_CRYPT
Bram Moolenaar8767f522016-07-01 17:17:39 +0200267static cryptstate_T *ml_crypt_prepare(memfile_T *mfp, off_T offset, int reading);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200268#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000269#ifdef FEAT_BYTEOFF
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100270static void ml_updatechunk(buf_T *buf, long line, long len, int updtype);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000271#endif
272
273/*
Bram Moolenaar4770d092006-01-12 23:22:24 +0000274 * Open a new memline for "buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +0000275 *
Bram Moolenaar4770d092006-01-12 23:22:24 +0000276 * Return FAIL for failure, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000277 */
278 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100279ml_open(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000280{
281 memfile_T *mfp;
282 bhdr_T *hp = NULL;
283 ZERO_BL *b0p;
284 PTR_BL *pp;
285 DATA_BL *dp;
286
Bram Moolenaar4770d092006-01-12 23:22:24 +0000287 /*
288 * init fields in memline struct
289 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100290 buf->b_ml.ml_stack_size = 0; // no stack yet
291 buf->b_ml.ml_stack = NULL; // no stack yet
292 buf->b_ml.ml_stack_top = 0; // nothing in the stack
293 buf->b_ml.ml_locked = NULL; // no cached block
294 buf->b_ml.ml_line_lnum = 0; // no cached line
Bram Moolenaar071d4272004-06-13 20:20:40 +0000295#ifdef FEAT_BYTEOFF
Bram Moolenaar4770d092006-01-12 23:22:24 +0000296 buf->b_ml.ml_chunksize = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000297#endif
298
Bram Moolenaare1004402020-10-24 20:49:43 +0200299 if (cmdmod.cmod_flags & CMOD_NOSWAPFILE)
Bram Moolenaar5803ae62014-03-23 16:04:02 +0100300 buf->b_p_swf = FALSE;
301
Bram Moolenaar4770d092006-01-12 23:22:24 +0000302 /*
303 * When 'updatecount' is non-zero swap file may be opened later.
304 */
305 if (p_uc && buf->b_p_swf)
306 buf->b_may_swap = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000307 else
Bram Moolenaar4770d092006-01-12 23:22:24 +0000308 buf->b_may_swap = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000309
Bram Moolenaar4770d092006-01-12 23:22:24 +0000310 /*
311 * Open the memfile. No swap file is created yet.
312 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000313 mfp = mf_open(NULL, 0);
314 if (mfp == NULL)
315 goto error;
316
Bram Moolenaar4770d092006-01-12 23:22:24 +0000317 buf->b_ml.ml_mfp = mfp;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200318#ifdef FEAT_CRYPT
319 mfp->mf_buffer = buf;
320#endif
Bram Moolenaar4770d092006-01-12 23:22:24 +0000321 buf->b_ml.ml_flags = ML_EMPTY;
322 buf->b_ml.ml_line_count = 1;
Bram Moolenaar592e0a22004-07-03 16:05:59 +0000323#ifdef FEAT_LINEBREAK
324 curwin->w_nrwidth_line_count = 0;
325#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000326
Bram Moolenaar071d4272004-06-13 20:20:40 +0000327/*
328 * fill block0 struct and write page 0
329 */
330 if ((hp = mf_new(mfp, FALSE, 1)) == NULL)
331 goto error;
332 if (hp->bh_bnum != 0)
333 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100334 iemsg(_("E298: Didn't get block nr 0?"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000335 goto error;
336 }
337 b0p = (ZERO_BL *)(hp->bh_data);
338
339 b0p->b0_id[0] = BLOCK0_ID0;
340 b0p->b0_id[1] = BLOCK0_ID1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000341 b0p->b0_magic_long = (long)B0_MAGIC_LONG;
342 b0p->b0_magic_int = (int)B0_MAGIC_INT;
343 b0p->b0_magic_short = (short)B0_MAGIC_SHORT;
344 b0p->b0_magic_char = B0_MAGIC_CHAR;
Bram Moolenaar22c10562018-05-26 17:35:27 +0200345 mch_memmove(b0p->b0_version, "VIM ", 4);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000346 STRNCPY(b0p->b0_version + 4, Version, 6);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000347 long_to_char((long)mfp->mf_page_size, b0p->b0_page_size);
Bram Moolenaar4770d092006-01-12 23:22:24 +0000348
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000349#ifdef FEAT_SPELL
350 if (!buf->b_spell)
351#endif
Bram Moolenaar4770d092006-01-12 23:22:24 +0000352 {
353 b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0;
354 b0p->b0_flags = get_fileformat(buf) + 1;
355 set_b0_fname(b0p, buf);
356 (void)get_user_name(b0p->b0_uname, B0_UNAME_SIZE);
357 b0p->b0_uname[B0_UNAME_SIZE - 1] = NUL;
358 mch_get_host_name(b0p->b0_hname, B0_HNAME_SIZE);
359 b0p->b0_hname[B0_HNAME_SIZE - 1] = NUL;
360 long_to_char(mch_get_pid(), b0p->b0_pid);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200361#ifdef FEAT_CRYPT
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200362 ml_set_b0_crypt(buf, b0p);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200363#endif
Bram Moolenaar4770d092006-01-12 23:22:24 +0000364 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000365
366 /*
367 * Always sync block number 0 to disk, so we can check the file name in
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200368 * the swap file in findswapname(). Don't do this for a help files or
369 * a spell buffer though.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000370 * Only works when there's a swapfile, otherwise it's done when the file
371 * is created.
372 */
373 mf_put(mfp, hp, TRUE, FALSE);
Bram Moolenaar4770d092006-01-12 23:22:24 +0000374 if (!buf->b_help && !B_SPELL(buf))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000375 (void)mf_sync(mfp, 0);
376
Bram Moolenaar4770d092006-01-12 23:22:24 +0000377 /*
378 * Fill in root pointer block and write page 1.
379 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000380 if ((hp = ml_new_ptr(mfp)) == NULL)
381 goto error;
382 if (hp->bh_bnum != 1)
383 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100384 iemsg(_("E298: Didn't get block nr 1?"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000385 goto error;
386 }
387 pp = (PTR_BL *)(hp->bh_data);
388 pp->pb_count = 1;
389 pp->pb_pointer[0].pe_bnum = 2;
390 pp->pb_pointer[0].pe_page_count = 1;
391 pp->pb_pointer[0].pe_old_lnum = 1;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100392 pp->pb_pointer[0].pe_line_count = 1; // line count after insertion
Bram Moolenaar071d4272004-06-13 20:20:40 +0000393 mf_put(mfp, hp, TRUE, FALSE);
394
Bram Moolenaar4770d092006-01-12 23:22:24 +0000395 /*
396 * Allocate first data block and create an empty line 1.
397 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000398 if ((hp = ml_new_data(mfp, FALSE, 1)) == NULL)
399 goto error;
400 if (hp->bh_bnum != 2)
401 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100402 iemsg(_("E298: Didn't get block nr 2?"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000403 goto error;
404 }
405
406 dp = (DATA_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100407 dp->db_index[0] = --dp->db_txt_start; // at end of block
Bram Moolenaar071d4272004-06-13 20:20:40 +0000408 dp->db_free -= 1 + INDEX_SIZE;
409 dp->db_line_count = 1;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100410 *((char_u *)dp + dp->db_txt_start) = NUL; // empty line
Bram Moolenaar071d4272004-06-13 20:20:40 +0000411
412 return OK;
413
414error:
415 if (mfp != NULL)
416 {
417 if (hp)
418 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100419 mf_close(mfp, TRUE); // will also free(mfp->mf_fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000420 }
Bram Moolenaar4770d092006-01-12 23:22:24 +0000421 buf->b_ml.ml_mfp = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000422 return FAIL;
423}
424
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200425#if defined(FEAT_CRYPT) || defined(PROTO)
426/*
Bram Moolenaar2be79502014-08-13 21:58:28 +0200427 * Prepare encryption for "buf" for the current key and method.
428 */
429 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100430ml_set_mfp_crypt(buf_T *buf)
Bram Moolenaar2be79502014-08-13 21:58:28 +0200431{
432 if (*buf->b_p_key != NUL)
433 {
434 int method_nr = crypt_get_method_nr(buf);
435
Christian Brabandtf573c6e2021-06-20 14:02:16 +0200436 if (method_nr > CRYPT_M_ZIP && method_nr < CRYPT_M_SOD)
Bram Moolenaar2be79502014-08-13 21:58:28 +0200437 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100438 // Generate a seed and store it in the memfile.
Bram Moolenaar2be79502014-08-13 21:58:28 +0200439 sha2_seed(buf->b_ml.ml_mfp->mf_seed, MF_SEED_LEN, NULL, 0);
440 }
Christian Brabandtf573c6e2021-06-20 14:02:16 +0200441#ifdef FEAT_SODIUM
442 else if (method_nr == CRYPT_M_SOD)
443 randombytes_buf(buf->b_ml.ml_mfp->mf_seed, MF_SEED_LEN);
444 #endif
Bram Moolenaar2be79502014-08-13 21:58:28 +0200445 }
446}
447
448/*
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200449 * Prepare encryption for "buf" with block 0 "b0p".
450 */
451 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100452ml_set_b0_crypt(buf_T *buf, ZERO_BL *b0p)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200453{
454 if (*buf->b_p_key == NUL)
455 b0p->b0_id[1] = BLOCK0_ID1;
456 else
457 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200458 int method_nr = crypt_get_method_nr(buf);
459
460 b0p->b0_id[1] = id1_codes[method_nr];
Christian Brabandtf573c6e2021-06-20 14:02:16 +0200461 if (method_nr > CRYPT_M_ZIP && method_nr < CRYPT_M_SOD)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200462 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100463 // Generate a seed and store it in block 0 and in the memfile.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200464 sha2_seed(&b0p->b0_seed, MF_SEED_LEN, NULL, 0);
465 mch_memmove(buf->b_ml.ml_mfp->mf_seed, &b0p->b0_seed, MF_SEED_LEN);
466 }
467 }
468}
469
470/*
471 * Called after the crypt key or 'cryptmethod' was changed for "buf".
472 * Will apply this to the swapfile.
473 * "old_key" is the previous key. It is equal to buf->b_p_key when
474 * 'cryptmethod' is changed.
Bram Moolenaar49771f42010-07-20 17:32:38 +0200475 * "old_cm" is the previous 'cryptmethod'. It is equal to the current
476 * 'cryptmethod' when 'key' is changed.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200477 */
478 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100479ml_set_crypt_key(
480 buf_T *buf,
481 char_u *old_key,
482 char_u *old_cm)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200483{
484 memfile_T *mfp = buf->b_ml.ml_mfp;
485 bhdr_T *hp;
486 int page_count;
487 int idx;
488 long error;
489 infoptr_T *ip;
490 PTR_BL *pp;
491 DATA_BL *dp;
492 blocknr_T bnum;
493 int top;
Bram Moolenaarbc563362015-06-09 18:35:25 +0200494 int old_method;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200495
Christian Brabandtf573c6e2021-06-20 14:02:16 +0200496 if (mfp == NULL || mfp->mf_fd < 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100497 return; // no memfile yet, nothing to do
Bram Moolenaarbc563362015-06-09 18:35:25 +0200498 old_method = crypt_method_nr_from_name(old_cm);
499
Christian Brabandt226b28b2021-06-21 21:08:08 +0200500 // Swapfile encryption not supported by XChaCha20
501 if (crypt_get_method_nr(buf) == CRYPT_M_SOD && *buf->b_p_key != NUL)
Christian Brabandtf573c6e2021-06-20 14:02:16 +0200502 {
503 // close the swapfile
504 mf_close_file(buf, TRUE);
505 buf->b_p_swf = FALSE;
506 return;
507 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100508 // First make sure the swapfile is in a consistent state, using the old
509 // key and method.
Bram Moolenaarbc563362015-06-09 18:35:25 +0200510 {
511 char_u *new_key = buf->b_p_key;
512 char_u *new_buf_cm = buf->b_p_cm;
513
514 buf->b_p_key = old_key;
515 buf->b_p_cm = old_cm;
516 ml_preserve(buf, FALSE);
517 buf->b_p_key = new_key;
518 buf->b_p_cm = new_buf_cm;
519 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200520
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100521 // Set the key, method and seed to be used for reading, these must be the
522 // old values.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200523 mfp->mf_old_key = old_key;
Bram Moolenaarbc563362015-06-09 18:35:25 +0200524 mfp->mf_old_cm = old_method;
525 if (old_method > 0 && *old_key != NUL)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200526 mch_memmove(mfp->mf_old_seed, mfp->mf_seed, MF_SEED_LEN);
527
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100528 // Update block 0 with the crypt flag and may set a new seed.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200529 ml_upd_block0(buf, UB_CRYPT);
530
531 if (mfp->mf_infile_count > 2)
532 {
533 /*
534 * Need to read back all data blocks from disk, decrypt them with the
535 * old key/method and mark them to be written. The algorithm is
536 * similar to what happens in ml_recover(), but we skip negative block
537 * numbers.
538 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100539 ml_flush_line(buf); // flush buffered line
540 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush locked block
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200541
542 hp = NULL;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100543 bnum = 1; // start with block 1
544 page_count = 1; // which is 1 page
545 idx = 0; // start with first index in block 1
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200546 error = 0;
547 buf->b_ml.ml_stack_top = 0;
Bram Moolenaard23a8232018-02-10 18:45:26 +0100548 VIM_CLEAR(buf->b_ml.ml_stack);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100549 buf->b_ml.ml_stack_size = 0; // no stack yet
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200550
551 for ( ; !got_int; line_breakcheck())
552 {
553 if (hp != NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100554 mf_put(mfp, hp, FALSE, FALSE); // release previous block
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200555
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100556 // get the block (pointer or data)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200557 if ((hp = mf_get(mfp, (blocknr_T)bnum, page_count)) == NULL)
558 {
559 if (bnum == 1)
560 break;
561 ++error;
562 }
563 else
564 {
565 pp = (PTR_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100566 if (pp->pb_id == PTR_ID) // it is a pointer block
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200567 {
568 if (pp->pb_count == 0)
569 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100570 // empty block?
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200571 ++error;
572 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100573 else if (idx < (int)pp->pb_count) // go a block deeper
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200574 {
575 if (pp->pb_pointer[idx].pe_bnum < 0)
576 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100577 // Skip data block with negative block number.
578 // Should not happen, because of the ml_preserve()
579 // above. Get same block again for next index.
Bram Moolenaar4b7214e2019-01-03 21:55:32 +0100580 ++idx;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200581 continue;
582 }
583
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100584 // going one block deeper in the tree, new entry in
585 // stack
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200586 if ((top = ml_add_stack(buf)) < 0)
587 {
588 ++error;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100589 break; // out of memory
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200590 }
591 ip = &(buf->b_ml.ml_stack[top]);
592 ip->ip_bnum = bnum;
593 ip->ip_index = idx;
594
595 bnum = pp->pb_pointer[idx].pe_bnum;
596 page_count = pp->pb_pointer[idx].pe_page_count;
Bram Moolenaarbc563362015-06-09 18:35:25 +0200597 idx = 0;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200598 continue;
599 }
600 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100601 else // not a pointer block
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200602 {
603 dp = (DATA_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100604 if (dp->db_id != DATA_ID) // block id wrong
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200605 ++error;
606 else
607 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100608 // It is a data block, need to write it back to disk.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200609 mf_put(mfp, hp, TRUE, FALSE);
610 hp = NULL;
611 }
612 }
613 }
614
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100615 if (buf->b_ml.ml_stack_top == 0) // finished
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200616 break;
617
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100618 // go one block up in the tree
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200619 ip = &(buf->b_ml.ml_stack[--(buf->b_ml.ml_stack_top)]);
620 bnum = ip->ip_bnum;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100621 idx = ip->ip_index + 1; // go to next index
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200622 page_count = 1;
623 }
Bram Moolenaarbc563362015-06-09 18:35:25 +0200624 if (hp != NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100625 mf_put(mfp, hp, FALSE, FALSE); // release previous block
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +0100626
627 if (error > 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100628 emsg(_("E843: Error while updating swap file crypt"));
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200629 }
630
631 mfp->mf_old_key = NULL;
632}
633#endif
634
Bram Moolenaar071d4272004-06-13 20:20:40 +0000635/*
636 * ml_setname() is called when the file name of "buf" has been changed.
637 * It may rename the swap file.
638 */
639 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100640ml_setname(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000641{
642 int success = FALSE;
643 memfile_T *mfp;
644 char_u *fname;
645 char_u *dirp;
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100646#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000647 char_u *p;
648#endif
649
650 mfp = buf->b_ml.ml_mfp;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100651 if (mfp->mf_fd < 0) // there is no swap file yet
Bram Moolenaar071d4272004-06-13 20:20:40 +0000652 {
653 /*
654 * When 'updatecount' is 0 and 'noswapfile' there is no swap file.
655 * For help files we will make a swap file now.
656 */
Bram Moolenaare1004402020-10-24 20:49:43 +0200657 if (p_uc != 0 && (cmdmod.cmod_flags & CMOD_NOSWAPFILE) == 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100658 ml_open_file(buf); // create a swap file
Bram Moolenaar071d4272004-06-13 20:20:40 +0000659 return;
660 }
661
662 /*
663 * Try all directories in the 'directory' option.
664 */
665 dirp = p_dir;
666 for (;;)
667 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100668 if (*dirp == NUL) // tried all directories, fail
Bram Moolenaar071d4272004-06-13 20:20:40 +0000669 break;
Bram Moolenaar8fc061c2004-12-29 21:03:02 +0000670 fname = findswapname(buf, &dirp, mfp->mf_fname);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100671 // alloc's fname
672 if (dirp == NULL) // out of memory
Bram Moolenaarf541c362011-10-26 11:44:18 +0200673 break;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100674 if (fname == NULL) // no file name found for this dir
Bram Moolenaar071d4272004-06-13 20:20:40 +0000675 continue;
676
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100677#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000678 /*
679 * Set full pathname for swap file now, because a ":!cd dir" may
680 * change directory without us knowing it.
681 */
682 p = FullName_save(fname, FALSE);
683 vim_free(fname);
684 fname = p;
685 if (fname == NULL)
686 continue;
687#endif
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100688 // if the file name is the same we don't have to do anything
Bram Moolenaar071d4272004-06-13 20:20:40 +0000689 if (fnamecmp(fname, mfp->mf_fname) == 0)
690 {
691 vim_free(fname);
692 success = TRUE;
693 break;
694 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100695 // need to close the swap file before renaming
Bram Moolenaar071d4272004-06-13 20:20:40 +0000696 if (mfp->mf_fd >= 0)
697 {
698 close(mfp->mf_fd);
699 mfp->mf_fd = -1;
700 }
701
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100702 // try to rename the swap file
Bram Moolenaar071d4272004-06-13 20:20:40 +0000703 if (vim_rename(mfp->mf_fname, fname) == 0)
704 {
705 success = TRUE;
706 vim_free(mfp->mf_fname);
707 mfp->mf_fname = fname;
708 vim_free(mfp->mf_ffname);
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100709#if defined(MSWIN)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100710 mfp->mf_ffname = NULL; // mf_fname is full pathname already
Bram Moolenaar071d4272004-06-13 20:20:40 +0000711#else
712 mf_set_ffname(mfp);
713#endif
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200714 ml_upd_block0(buf, UB_SAME_DIR);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000715 break;
716 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100717 vim_free(fname); // this fname didn't work, try another
Bram Moolenaar071d4272004-06-13 20:20:40 +0000718 }
719
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100720 if (mfp->mf_fd == -1) // need to (re)open the swap file
Bram Moolenaar071d4272004-06-13 20:20:40 +0000721 {
722 mfp->mf_fd = mch_open((char *)mfp->mf_fname, O_RDWR | O_EXTRA, 0);
723 if (mfp->mf_fd < 0)
724 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100725 // could not (re)open the swap file, what can we do????
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100726 emsg(_("E301: Oops, lost the swap file!!!"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000727 return;
728 }
Bram Moolenaarf05da212009-11-17 16:13:15 +0000729#ifdef HAVE_FD_CLOEXEC
730 {
731 int fdflags = fcntl(mfp->mf_fd, F_GETFD);
732 if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0)
Bram Moolenaarfbc4b4d2016-02-07 15:14:01 +0100733 (void)fcntl(mfp->mf_fd, F_SETFD, fdflags | FD_CLOEXEC);
Bram Moolenaarf05da212009-11-17 16:13:15 +0000734 }
735#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000736 }
737 if (!success)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100738 emsg(_("E302: Could not rename swap file"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000739}
740
741/*
742 * Open a file for the memfile for all buffers that are not readonly or have
743 * been modified.
744 * Used when 'updatecount' changes from zero to non-zero.
745 */
746 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100747ml_open_files(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000748{
749 buf_T *buf;
750
Bram Moolenaar29323592016-07-24 22:04:11 +0200751 FOR_ALL_BUFFERS(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000752 if (!buf->b_p_ro || buf->b_changed)
753 ml_open_file(buf);
754}
755
756/*
757 * Open a swap file for an existing memfile, if there is no swap file yet.
758 * If we are unable to find a file name, mf_fname will be NULL
759 * and the memfile will be in memory only (no recovery possible).
760 */
761 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100762ml_open_file(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000763{
764 memfile_T *mfp;
765 char_u *fname;
766 char_u *dirp;
767
768 mfp = buf->b_ml.ml_mfp;
Bram Moolenaare1004402020-10-24 20:49:43 +0200769 if (mfp == NULL || mfp->mf_fd >= 0 || !buf->b_p_swf
770 || (cmdmod.cmod_flags & CMOD_NOSWAPFILE))
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100771 return; // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +0000772
Bram Moolenaara1956f62006-03-12 22:18:00 +0000773#ifdef FEAT_SPELL
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100774 // For a spell buffer use a temp file name.
Bram Moolenaar4770d092006-01-12 23:22:24 +0000775 if (buf->b_spell)
776 {
Bram Moolenaare5c421c2015-03-31 13:33:08 +0200777 fname = vim_tempname('s', FALSE);
Bram Moolenaar4770d092006-01-12 23:22:24 +0000778 if (fname != NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100779 (void)mf_open_file(mfp, fname); // consumes fname!
Bram Moolenaar4770d092006-01-12 23:22:24 +0000780 buf->b_may_swap = FALSE;
781 return;
782 }
783#endif
784
Bram Moolenaar071d4272004-06-13 20:20:40 +0000785 /*
786 * Try all directories in 'directory' option.
787 */
788 dirp = p_dir;
789 for (;;)
790 {
791 if (*dirp == NUL)
792 break;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100793 // There is a small chance that between choosing the swap file name
794 // and creating it, another Vim creates the file. In that case the
795 // creation will fail and we will use another directory.
796 fname = findswapname(buf, &dirp, NULL); // allocates fname
Bram Moolenaarf541c362011-10-26 11:44:18 +0200797 if (dirp == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100798 break; // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +0000799 if (fname == NULL)
800 continue;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100801 if (mf_open_file(mfp, fname) == OK) // consumes fname!
Bram Moolenaar071d4272004-06-13 20:20:40 +0000802 {
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100803#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000804 /*
805 * set full pathname for swap file now, because a ":!cd dir" may
806 * change directory without us knowing it.
807 */
808 mf_fullname(mfp);
809#endif
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200810 ml_upd_block0(buf, UB_SAME_DIR);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000811
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100812 // Flush block zero, so others can read it
Bram Moolenaar071d4272004-06-13 20:20:40 +0000813 if (mf_sync(mfp, MFS_ZERO) == OK)
Bram Moolenaarc32840f2006-01-14 21:23:38 +0000814 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100815 // Mark all blocks that should be in the swapfile as dirty.
816 // Needed for when the 'swapfile' option was reset, so that
817 // the swap file was deleted, and then on again.
Bram Moolenaarc32840f2006-01-14 21:23:38 +0000818 mf_set_dirty(mfp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000819 break;
Bram Moolenaarc32840f2006-01-14 21:23:38 +0000820 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100821 // Writing block 0 failed: close the file and try another dir
Bram Moolenaar071d4272004-06-13 20:20:40 +0000822 mf_close_file(buf, FALSE);
823 }
824 }
825
Bram Moolenaar00e192b2019-10-19 17:01:28 +0200826 if (*p_dir != NUL && mfp->mf_fname == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000827 {
Bram Moolenaar00e192b2019-10-19 17:01:28 +0200828 need_wait_return = TRUE; // call wait_return later
Bram Moolenaar071d4272004-06-13 20:20:40 +0000829 ++no_wait_return;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100830 (void)semsg(_("E303: Unable to open swap file for \"%s\", recovery impossible"),
Bram Moolenaare1704ba2012-10-03 18:25:00 +0200831 buf_spname(buf) != NULL ? buf_spname(buf) : buf->b_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000832 --no_wait_return;
833 }
834
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100835 // don't try to open a swap file again
Bram Moolenaar071d4272004-06-13 20:20:40 +0000836 buf->b_may_swap = FALSE;
837}
838
839/*
840 * If still need to create a swap file, and starting to edit a not-readonly
841 * file, or reading into an existing buffer, create a swap file now.
842 */
843 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100844check_need_swap(
Bram Moolenaar2f0f8712018-08-21 18:50:18 +0200845 int newfile) // reading file into new buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +0000846{
Bram Moolenaar2f0f8712018-08-21 18:50:18 +0200847 int old_msg_silent = msg_silent; // might be reset by an E325 message
848
Bram Moolenaar071d4272004-06-13 20:20:40 +0000849 if (curbuf->b_may_swap && (!curbuf->b_p_ro || !newfile))
850 ml_open_file(curbuf);
Bram Moolenaar2f0f8712018-08-21 18:50:18 +0200851 msg_silent = old_msg_silent;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000852}
853
854/*
855 * Close memline for buffer 'buf'.
856 * If 'del_file' is TRUE, delete the swap file
857 */
858 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100859ml_close(buf_T *buf, int del_file)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000860{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100861 if (buf->b_ml.ml_mfp == NULL) // not open
Bram Moolenaar071d4272004-06-13 20:20:40 +0000862 return;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100863 mf_close(buf->b_ml.ml_mfp, del_file); // close the .swp file
Bram Moolenaar071d4272004-06-13 20:20:40 +0000864 if (buf->b_ml.ml_line_lnum != 0 && (buf->b_ml.ml_flags & ML_LINE_DIRTY))
865 vim_free(buf->b_ml.ml_line_ptr);
866 vim_free(buf->b_ml.ml_stack);
867#ifdef FEAT_BYTEOFF
Bram Moolenaard23a8232018-02-10 18:45:26 +0100868 VIM_CLEAR(buf->b_ml.ml_chunksize);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000869#endif
870 buf->b_ml.ml_mfp = NULL;
871
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100872 // Reset the "recovered" flag, give the ATTENTION prompt the next time
873 // this buffer is loaded.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000874 buf->b_flags &= ~BF_RECOVERED;
875}
876
877/*
878 * Close all existing memlines and memfiles.
879 * Only used when exiting.
880 * When 'del_file' is TRUE, delete the memfiles.
Bram Moolenaar81bf7082005-02-12 14:31:42 +0000881 * But don't delete files that were ":preserve"d when we are POSIX compatible.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000882 */
883 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100884ml_close_all(int del_file)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000885{
886 buf_T *buf;
887
Bram Moolenaar29323592016-07-24 22:04:11 +0200888 FOR_ALL_BUFFERS(buf)
Bram Moolenaar81bf7082005-02-12 14:31:42 +0000889 ml_close(buf, del_file && ((buf->b_flags & BF_PRESERVED) == 0
890 || vim_strchr(p_cpo, CPO_PRESERVE) == NULL));
Bram Moolenaar34b466e2013-11-28 17:41:46 +0100891#ifdef FEAT_SPELL
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100892 spell_delete_wordlist(); // delete the internal wordlist
Bram Moolenaar34b466e2013-11-28 17:41:46 +0100893#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000894#ifdef TEMPDIRNAMES
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100895 vim_deltempdir(); // delete created temp directory
Bram Moolenaar071d4272004-06-13 20:20:40 +0000896#endif
897}
898
899/*
900 * Close all memfiles for not modified buffers.
901 * Only use just before exiting!
902 */
903 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100904ml_close_notmod(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000905{
906 buf_T *buf;
907
Bram Moolenaar29323592016-07-24 22:04:11 +0200908 FOR_ALL_BUFFERS(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000909 if (!bufIsChanged(buf))
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100910 ml_close(buf, TRUE); // close all not-modified buffers
Bram Moolenaar071d4272004-06-13 20:20:40 +0000911}
912
913/*
914 * Update the timestamp in the .swp file.
915 * Used when the file has been written.
916 */
917 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100918ml_timestamp(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000919{
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200920 ml_upd_block0(buf, UB_FNAME);
921}
922
923/*
924 * Return FAIL when the ID of "b0p" is wrong.
925 */
926 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100927ml_check_b0_id(ZERO_BL *b0p)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200928{
929 if (b0p->b0_id[0] != BLOCK0_ID0
930 || (b0p->b0_id[1] != BLOCK0_ID1
931 && b0p->b0_id[1] != BLOCK0_ID1_C0
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200932 && b0p->b0_id[1] != BLOCK0_ID1_C1
Christian Brabandtf573c6e2021-06-20 14:02:16 +0200933 && b0p->b0_id[1] != BLOCK0_ID1_C2
934 && b0p->b0_id[1] != BLOCK0_ID1_C3)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200935 )
936 return FAIL;
937 return OK;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000938}
939
940/*
941 * Update the timestamp or the B0_SAME_DIR flag of the .swp file.
942 */
943 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100944ml_upd_block0(buf_T *buf, upd_block0_T what)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000945{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000946 memfile_T *mfp;
947 bhdr_T *hp;
948 ZERO_BL *b0p;
949
950 mfp = buf->b_ml.ml_mfp;
Bram Moolenaar2be79502014-08-13 21:58:28 +0200951 if (mfp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000952 return;
Bram Moolenaar2be79502014-08-13 21:58:28 +0200953 hp = mf_get(mfp, (blocknr_T)0, 1);
954 if (hp == NULL)
955 {
956#ifdef FEAT_CRYPT
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100957 // Possibly update the seed in the memfile before there is a block0.
Bram Moolenaar2be79502014-08-13 21:58:28 +0200958 if (what == UB_CRYPT)
959 ml_set_mfp_crypt(buf);
960#endif
961 return;
962 }
963
Bram Moolenaar071d4272004-06-13 20:20:40 +0000964 b0p = (ZERO_BL *)(hp->bh_data);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200965 if (ml_check_b0_id(b0p) == FAIL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100966 iemsg(_("E304: ml_upd_block0(): Didn't get block 0??"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000967 else
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000968 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200969 if (what == UB_FNAME)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000970 set_b0_fname(b0p, buf);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200971#ifdef FEAT_CRYPT
972 else if (what == UB_CRYPT)
973 ml_set_b0_crypt(buf, b0p);
974#endif
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100975 else // what == UB_SAME_DIR
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000976 set_b0_dir_flag(b0p, buf);
977 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000978 mf_put(mfp, hp, TRUE, FALSE);
979}
980
981/*
982 * Write file name and timestamp into block 0 of a swap file.
983 * Also set buf->b_mtime.
984 * Don't use NameBuff[]!!!
985 */
986 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100987set_b0_fname(ZERO_BL *b0p, buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000988{
Bram Moolenaar8767f522016-07-01 17:17:39 +0200989 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000990
991 if (buf->b_ffname == NULL)
992 b0p->b0_fname[0] = NUL;
993 else
994 {
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100995#if defined(MSWIN) || defined(AMIGA)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100996 // Systems that cannot translate "~user" back into a path: copy the
997 // file name unmodified. Do use slashes instead of backslashes for
998 // portability.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200999 vim_strncpy(b0p->b0_fname, buf->b_ffname, B0_FNAME_SIZE_CRYPT - 1);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001000# ifdef BACKSLASH_IN_FILENAME
1001 forward_slash(b0p->b0_fname);
1002# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001003#else
1004 size_t flen, ulen;
1005 char_u uname[B0_UNAME_SIZE];
1006
1007 /*
1008 * For a file under the home directory of the current user, we try to
1009 * replace the home directory path with "~user". This helps when
1010 * editing the same file on different machines over a network.
1011 * First replace home dir path with "~/" with home_replace().
1012 * Then insert the user name to get "~user/".
1013 */
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001014 home_replace(NULL, buf->b_ffname, b0p->b0_fname,
1015 B0_FNAME_SIZE_CRYPT, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001016 if (b0p->b0_fname[0] == '~')
1017 {
1018 flen = STRLEN(b0p->b0_fname);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001019 // If there is no user name or it is too long, don't use "~/"
Bram Moolenaar071d4272004-06-13 20:20:40 +00001020 if (get_user_name(uname, B0_UNAME_SIZE) == FAIL
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001021 || (ulen = STRLEN(uname)) + flen > B0_FNAME_SIZE_CRYPT - 1)
1022 vim_strncpy(b0p->b0_fname, buf->b_ffname,
1023 B0_FNAME_SIZE_CRYPT - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001024 else
1025 {
1026 mch_memmove(b0p->b0_fname + ulen + 1, b0p->b0_fname + 1, flen);
1027 mch_memmove(b0p->b0_fname + 1, uname, ulen);
1028 }
1029 }
1030#endif
1031 if (mch_stat((char *)buf->b_ffname, &st) >= 0)
1032 {
1033 long_to_char((long)st.st_mtime, b0p->b0_mtime);
1034#ifdef CHECK_INODE
1035 long_to_char((long)st.st_ino, b0p->b0_ino);
1036#endif
1037 buf_store_time(buf, &st, buf->b_ffname);
1038 buf->b_mtime_read = buf->b_mtime;
1039 }
1040 else
1041 {
1042 long_to_char(0L, b0p->b0_mtime);
1043#ifdef CHECK_INODE
1044 long_to_char(0L, b0p->b0_ino);
1045#endif
1046 buf->b_mtime = 0;
1047 buf->b_mtime_read = 0;
1048 buf->b_orig_size = 0;
1049 buf->b_orig_mode = 0;
1050 }
1051 }
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001052
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001053 // Also add the 'fileencoding' if there is room.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001054 add_b0_fenc(b0p, curbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001055}
1056
1057/*
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001058 * Update the B0_SAME_DIR flag of the swap file. It's set if the file and the
1059 * swapfile for "buf" are in the same directory.
1060 * This is fail safe: if we are not sure the directories are equal the flag is
1061 * not set.
1062 */
1063 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001064set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001065{
1066 if (same_directory(buf->b_ml.ml_mfp->mf_fname, buf->b_ffname))
1067 b0p->b0_flags |= B0_SAME_DIR;
1068 else
1069 b0p->b0_flags &= ~B0_SAME_DIR;
1070}
1071
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001072/*
1073 * When there is room, add the 'fileencoding' to block zero.
1074 */
1075 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001076add_b0_fenc(
1077 ZERO_BL *b0p,
1078 buf_T *buf)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001079{
1080 int n;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001081 int size = B0_FNAME_SIZE_NOCRYPT;
1082
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01001083#ifdef FEAT_CRYPT
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001084 // Without encryption use the same offset as in Vim 7.2 to be compatible.
1085 // With encryption it's OK to move elsewhere, the swap file is not
1086 // compatible anyway.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001087 if (*buf->b_p_key != NUL)
1088 size = B0_FNAME_SIZE_CRYPT;
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01001089#endif
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001090
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00001091 n = (int)STRLEN(buf->b_p_fenc);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001092 if ((int)STRLEN(b0p->b0_fname) + n + 1 > size)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001093 b0p->b0_flags &= ~B0_HAS_FENC;
1094 else
1095 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001096 mch_memmove((char *)b0p->b0_fname + size - n,
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001097 (char *)buf->b_p_fenc, (size_t)n);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001098 *(b0p->b0_fname + size - n - 1) = NUL;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001099 b0p->b0_flags |= B0_HAS_FENC;
1100 }
1101}
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001102
Bram Moolenaarf52f0602021-03-10 21:26:37 +01001103#if defined(HAVE_SYS_SYSINFO_H) && defined(HAVE_SYSINFO_UPTIME)
1104# include <sys/sysinfo.h>
1105#endif
1106
1107/*
1108 * Return TRUE if the process with number "b0p->b0_pid" is still running.
1109 * "swap_fname" is the name of the swap file, if it's from before a reboot then
1110 * the result is FALSE;
1111 */
1112 static int
1113swapfile_process_running(ZERO_BL *b0p, char_u *swap_fname UNUSED)
1114{
1115#ifdef HAVE_SYSINFO_UPTIME
1116 stat_T st;
1117 struct sysinfo sinfo;
1118
1119 // If the system rebooted after when the swap file was written then the
1120 // process can't be running now.
1121 if (mch_stat((char *)swap_fname, &st) != -1
1122 && sysinfo(&sinfo) == 0
Bram Moolenaar23b32a82021-03-10 21:55:46 +01001123 && st.st_mtime < time(NULL) - (
1124# ifdef FEAT_EVAL
1125 override_sysinfo_uptime >= 0 ? override_sysinfo_uptime :
1126# endif
1127 sinfo.uptime))
Bram Moolenaarf52f0602021-03-10 21:26:37 +01001128 return FALSE;
1129#endif
1130 return mch_process_running(char_to_long(b0p->b0_pid));
1131}
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001132
1133/*
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001134 * Try to recover curbuf from the .swp file.
Bram Moolenaar99499b12019-05-23 21:35:48 +02001135 * If "checkext" is TRUE, check the extension and detect whether it is
1136 * a swap file.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001137 */
1138 void
Bram Moolenaar99499b12019-05-23 21:35:48 +02001139ml_recover(int checkext)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001140{
1141 buf_T *buf = NULL;
1142 memfile_T *mfp = NULL;
1143 char_u *fname;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001144 char_u *fname_used = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001145 bhdr_T *hp = NULL;
1146 ZERO_BL *b0p;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001147 int b0_ff;
1148 char_u *b0_fenc = NULL;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001149#ifdef FEAT_CRYPT
1150 int b0_cm = -1;
1151#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001152 PTR_BL *pp;
1153 DATA_BL *dp;
1154 infoptr_T *ip;
1155 blocknr_T bnum;
1156 int page_count;
Bram Moolenaar8767f522016-07-01 17:17:39 +02001157 stat_T org_stat, swp_stat;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001158 int len;
1159 int directly;
1160 linenr_T lnum;
1161 char_u *p;
1162 int i;
1163 long error;
1164 int cannot_open;
1165 linenr_T line_count;
1166 int has_error;
1167 int idx;
1168 int top;
1169 int txt_start;
Bram Moolenaar8767f522016-07-01 17:17:39 +02001170 off_T size;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001171 int called_from_main;
1172 int serious_error = TRUE;
1173 long mtime;
1174 int attr;
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001175 int orig_file_status = NOTDONE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001176
1177 recoverymode = TRUE;
1178 called_from_main = (curbuf->b_ml.ml_mfp == NULL);
Bram Moolenaar8820b482017-03-16 17:23:31 +01001179 attr = HL_ATTR(HLF_E);
Bram Moolenaard0ba34a2009-11-03 12:06:23 +00001180
1181 /*
Bram Moolenaaraf903e52017-12-02 15:11:22 +01001182 * If the file name ends in ".s[a-w][a-z]" we assume this is the swap file.
Bram Moolenaard0ba34a2009-11-03 12:06:23 +00001183 * Otherwise a search is done to find the swap file(s).
1184 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001185 fname = curbuf->b_fname;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001186 if (fname == NULL) // When there is no file name
Bram Moolenaar071d4272004-06-13 20:20:40 +00001187 fname = (char_u *)"";
1188 len = (int)STRLEN(fname);
Bram Moolenaar99499b12019-05-23 21:35:48 +02001189 if (checkext && len >= 4 &&
Bram Moolenaare60acc12011-05-10 16:41:25 +02001190#if defined(VMS)
Bram Moolenaar79518e22017-02-17 16:31:35 +01001191 STRNICMP(fname + len - 4, "_s", 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001192#else
Bram Moolenaar79518e22017-02-17 16:31:35 +01001193 STRNICMP(fname + len - 4, ".s", 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001194#endif
Bram Moolenaar79518e22017-02-17 16:31:35 +01001195 == 0
Bram Moolenaaraf903e52017-12-02 15:11:22 +01001196 && vim_strchr((char_u *)"abcdefghijklmnopqrstuvw",
1197 TOLOWER_ASC(fname[len - 2])) != NULL
Bram Moolenaard0ba34a2009-11-03 12:06:23 +00001198 && ASCII_ISALPHA(fname[len - 1]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001199 {
1200 directly = TRUE;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001201 fname_used = vim_strsave(fname); // make a copy for mf_open()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001202 }
1203 else
1204 {
1205 directly = FALSE;
1206
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001207 // count the number of matching swap files
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001208 len = recover_names(fname, FALSE, 0, NULL);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001209 if (len == 0) // no swap files found
Bram Moolenaar071d4272004-06-13 20:20:40 +00001210 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001211 semsg(_("E305: No swap file found for %s"), fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001212 goto theend;
1213 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001214 if (len == 1) // one swap file found, use it
Bram Moolenaar071d4272004-06-13 20:20:40 +00001215 i = 1;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001216 else // several swap files found, choose
Bram Moolenaar071d4272004-06-13 20:20:40 +00001217 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001218 // list the names of the swap files
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001219 (void)recover_names(fname, TRUE, 0, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001220 msg_putchar('\n');
Bram Moolenaar32526b32019-01-19 17:43:09 +01001221 msg_puts(_("Enter number of swap file to use (0 to quit): "));
Bram Moolenaar24bbcfe2005-06-28 23:32:02 +00001222 i = get_number(FALSE, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001223 if (i < 1 || i > len)
1224 goto theend;
1225 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001226 // get the swap file name that will be used
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001227 (void)recover_names(fname, FALSE, i, &fname_used);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001228 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001229 if (fname_used == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001230 goto theend; // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00001231
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001232 // When called from main() still need to initialize storage structure
Bram Moolenaar4770d092006-01-12 23:22:24 +00001233 if (called_from_main && ml_open(curbuf) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001234 getout(1);
1235
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001236 /*
1237 * Allocate a buffer structure for the swap file that is used for recovery.
Bram Moolenaar0ad014c2010-07-25 14:00:46 +02001238 * Only the memline and crypt information in it are really used.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001239 */
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001240 buf = ALLOC_ONE(buf_T);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001241 if (buf == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001242 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001243
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001244 /*
1245 * init fields in memline struct
1246 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001247 buf->b_ml.ml_stack_size = 0; // no stack yet
1248 buf->b_ml.ml_stack = NULL; // no stack yet
1249 buf->b_ml.ml_stack_top = 0; // nothing in the stack
1250 buf->b_ml.ml_line_lnum = 0; // no cached line
1251 buf->b_ml.ml_locked = NULL; // no locked block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001252 buf->b_ml.ml_flags = 0;
Bram Moolenaar0fe849a2010-07-25 15:11:11 +02001253#ifdef FEAT_CRYPT
1254 buf->b_p_key = empty_option;
1255 buf->b_p_cm = empty_option;
1256#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001257
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001258 /*
1259 * open the memfile from the old swap file
1260 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001261 p = vim_strsave(fname_used); // save "fname_used" for the message:
1262 // mf_open() will consume "fname_used"!
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001263 mfp = mf_open(fname_used, O_RDONLY);
1264 fname_used = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001265 if (mfp == NULL || mfp->mf_fd < 0)
1266 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001267 if (fname_used != NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001268 semsg(_("E306: Cannot open %s"), fname_used);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001269 goto theend;
1270 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001271 buf->b_ml.ml_mfp = mfp;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001272#ifdef FEAT_CRYPT
1273 mfp->mf_buffer = buf;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001274#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001275
1276 /*
1277 * The page size set in mf_open() might be different from the page size
1278 * used in the swap file, we must get it from block 0. But to read block
1279 * 0 we need a page size. Use the minimal size for block 0 here, it will
1280 * be set to the real value below.
1281 */
1282 mfp->mf_page_size = MIN_SWAP_PAGE_SIZE;
1283
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001284 /*
1285 * try to read block 0
1286 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001287 if ((hp = mf_get(mfp, (blocknr_T)0, 1)) == NULL)
1288 {
1289 msg_start();
Bram Moolenaar32526b32019-01-19 17:43:09 +01001290 msg_puts_attr(_("Unable to read block 0 from "), attr | MSG_HIST);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001291 msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001292 msg_puts_attr(_("\nMaybe no changes were made or Vim did not update the swap file."),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001293 attr | MSG_HIST);
1294 msg_end();
1295 goto theend;
1296 }
1297 b0p = (ZERO_BL *)(hp->bh_data);
1298 if (STRNCMP(b0p->b0_version, "VIM 3.0", 7) == 0)
1299 {
1300 msg_start();
1301 msg_outtrans_attr(mfp->mf_fname, MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001302 msg_puts_attr(_(" cannot be used with this version of Vim.\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001303 MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001304 msg_puts_attr(_("Use Vim version 3.0.\n"), MSG_HIST);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001305 msg_end();
1306 goto theend;
1307 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001308 if (ml_check_b0_id(b0p) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001309 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001310 semsg(_("E307: %s does not look like a Vim swap file"), mfp->mf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001311 goto theend;
1312 }
1313 if (b0_magic_wrong(b0p))
1314 {
1315 msg_start();
1316 msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
Bram Moolenaar48e330a2016-02-23 14:53:34 +01001317#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001318 if (STRNCMP(b0p->b0_hname, "PC ", 3) == 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001319 msg_puts_attr(_(" cannot be used with this version of Vim.\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001320 attr | MSG_HIST);
1321 else
1322#endif
Bram Moolenaar32526b32019-01-19 17:43:09 +01001323 msg_puts_attr(_(" cannot be used on this computer.\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001324 attr | MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001325 msg_puts_attr(_("The file was created on "), attr | MSG_HIST);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001326 // avoid going past the end of a corrupted hostname
Bram Moolenaar071d4272004-06-13 20:20:40 +00001327 b0p->b0_fname[0] = NUL;
Bram Moolenaar32526b32019-01-19 17:43:09 +01001328 msg_puts_attr((char *)b0p->b0_hname, attr | MSG_HIST);
1329 msg_puts_attr(_(",\nor the file has been damaged."), attr | MSG_HIST);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001330 msg_end();
1331 goto theend;
1332 }
Bram Moolenaar1c536282007-04-26 15:21:56 +00001333
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001334#ifdef FEAT_CRYPT
K.Takataeeec2542021-06-02 13:28:16 +02001335 for (i = 0; i < (int)ARRAY_LENGTH(id1_codes); ++i)
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001336 if (id1_codes[i] == b0p->b0_id[1])
1337 b0_cm = i;
1338 if (b0_cm > 0)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001339 mch_memmove(mfp->mf_seed, &b0p->b0_seed, MF_SEED_LEN);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001340 crypt_set_cm_option(buf, b0_cm < 0 ? 0 : b0_cm);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001341#else
1342 if (b0p->b0_id[1] != BLOCK0_ID1)
1343 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001344 semsg(_("E833: %s is encrypted and this version of Vim does not support encryption"), mfp->mf_fname);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001345 goto theend;
1346 }
1347#endif
1348
Bram Moolenaar071d4272004-06-13 20:20:40 +00001349 /*
1350 * If we guessed the wrong page size, we have to recalculate the
1351 * highest block number in the file.
1352 */
1353 if (mfp->mf_page_size != (unsigned)char_to_long(b0p->b0_page_size))
1354 {
Bram Moolenaar1c536282007-04-26 15:21:56 +00001355 unsigned previous_page_size = mfp->mf_page_size;
1356
Bram Moolenaar071d4272004-06-13 20:20:40 +00001357 mf_new_page_size(mfp, (unsigned)char_to_long(b0p->b0_page_size));
Bram Moolenaar1c536282007-04-26 15:21:56 +00001358 if (mfp->mf_page_size < previous_page_size)
1359 {
1360 msg_start();
1361 msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001362 msg_puts_attr(_(" has been damaged (page size is smaller than minimum value).\n"),
Bram Moolenaar1c536282007-04-26 15:21:56 +00001363 attr | MSG_HIST);
1364 msg_end();
1365 goto theend;
1366 }
Bram Moolenaar8767f522016-07-01 17:17:39 +02001367 if ((size = vim_lseek(mfp->mf_fd, (off_T)0L, SEEK_END)) <= 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001368 mfp->mf_blocknr_max = 0; // no file or empty file
Bram Moolenaar071d4272004-06-13 20:20:40 +00001369 else
1370 mfp->mf_blocknr_max = (blocknr_T)(size / mfp->mf_page_size);
1371 mfp->mf_infile_count = mfp->mf_blocknr_max;
Bram Moolenaar1c536282007-04-26 15:21:56 +00001372
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001373 // need to reallocate the memory used to store the data
Bram Moolenaar1c536282007-04-26 15:21:56 +00001374 p = alloc(mfp->mf_page_size);
1375 if (p == NULL)
1376 goto theend;
1377 mch_memmove(p, hp->bh_data, previous_page_size);
1378 vim_free(hp->bh_data);
1379 hp->bh_data = p;
1380 b0p = (ZERO_BL *)(hp->bh_data);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001381 }
1382
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001383 /*
1384 * If .swp file name given directly, use name from swap file for buffer.
1385 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001386 if (directly)
1387 {
1388 expand_env(b0p->b0_fname, NameBuff, MAXPATHL);
1389 if (setfname(curbuf, NameBuff, NULL, TRUE) == FAIL)
1390 goto theend;
1391 }
1392
1393 home_replace(NULL, mfp->mf_fname, NameBuff, MAXPATHL, TRUE);
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001394 smsg(_("Using swap file \"%s\""), NameBuff);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001395
1396 if (buf_spname(curbuf) != NULL)
Bram Moolenaare1704ba2012-10-03 18:25:00 +02001397 vim_strncpy(NameBuff, buf_spname(curbuf), MAXPATHL - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001398 else
1399 home_replace(NULL, curbuf->b_ffname, NameBuff, MAXPATHL, TRUE);
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001400 smsg(_("Original file \"%s\""), NameBuff);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001401 msg_putchar('\n');
1402
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001403 /*
1404 * check date of swap file and original file
1405 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001406 mtime = char_to_long(b0p->b0_mtime);
1407 if (curbuf->b_ffname != NULL
1408 && mch_stat((char *)curbuf->b_ffname, &org_stat) != -1
1409 && ((mch_stat((char *)mfp->mf_fname, &swp_stat) != -1
1410 && org_stat.st_mtime > swp_stat.st_mtime)
1411 || org_stat.st_mtime != mtime))
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001412 emsg(_("E308: Warning: Original file may have been changed"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001413 out_flush();
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001414
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001415 // Get the 'fileformat' and 'fileencoding' from block zero.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001416 b0_ff = (b0p->b0_flags & B0_FF_MASK);
1417 if (b0p->b0_flags & B0_HAS_FENC)
1418 {
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001419 int fnsize = B0_FNAME_SIZE_NOCRYPT;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001420
1421#ifdef FEAT_CRYPT
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001422 // Use the same size as in add_b0_fenc().
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001423 if (b0p->b0_id[1] != BLOCK0_ID1)
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001424 fnsize = B0_FNAME_SIZE_CRYPT;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001425#endif
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001426 for (p = b0p->b0_fname + fnsize; p > b0p->b0_fname && p[-1] != NUL; --p)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001427 ;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001428 b0_fenc = vim_strnsave(p, b0p->b0_fname + fnsize - p);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001429 }
1430
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001431 mf_put(mfp, hp, FALSE, FALSE); // release block 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001432 hp = NULL;
1433
1434 /*
1435 * Now that we are sure that the file is going to be recovered, clear the
1436 * contents of the current buffer.
1437 */
1438 while (!(curbuf->b_ml.ml_flags & ML_EMPTY))
Bram Moolenaarca70c072020-05-30 20:30:46 +02001439 ml_delete((linenr_T)1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001440
1441 /*
1442 * Try reading the original file to obtain the values of 'fileformat',
1443 * 'fileencoding', etc. Ignore errors. The text itself is not used.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001444 * When the file is encrypted the user is asked to enter the key.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001445 */
1446 if (curbuf->b_ffname != NULL)
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001447 orig_file_status = readfile(curbuf->b_ffname, NULL, (linenr_T)0,
Bram Moolenaar071d4272004-06-13 20:20:40 +00001448 (linenr_T)0, (linenr_T)MAXLNUM, NULL, READ_NEW);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001449
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001450#ifdef FEAT_CRYPT
1451 if (b0_cm >= 0)
1452 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001453 // Need to ask the user for the crypt key. If this fails we continue
1454 // without a key, will probably get garbage text.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001455 if (*curbuf->b_p_key != NUL)
1456 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001457 smsg(_("Swap file is encrypted: \"%s\""), fname_used);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001458 msg_puts(_("\nIf you entered a new crypt key but did not write the text file,"));
1459 msg_puts(_("\nenter the new crypt key."));
1460 msg_puts(_("\nIf you wrote the text file after changing the crypt key press enter"));
1461 msg_puts(_("\nto use the same key for text file and swap file"));
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001462 }
1463 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001464 smsg(_(need_key_msg), fname_used);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001465 buf->b_p_key = crypt_get_key(FALSE, FALSE);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001466 if (buf->b_p_key == NULL)
1467 buf->b_p_key = curbuf->b_p_key;
1468 else if (*buf->b_p_key == NUL)
1469 {
1470 vim_free(buf->b_p_key);
1471 buf->b_p_key = curbuf->b_p_key;
1472 }
1473 if (buf->b_p_key == NULL)
1474 buf->b_p_key = empty_option;
1475 }
1476#endif
1477
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001478 // Use the 'fileformat' and 'fileencoding' as stored in the swap file.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001479 if (b0_ff != 0)
1480 set_fileformat(b0_ff - 1, OPT_LOCAL);
1481 if (b0_fenc != NULL)
1482 {
1483 set_option_value((char_u *)"fenc", 0L, b0_fenc, OPT_LOCAL);
1484 vim_free(b0_fenc);
1485 }
Bram Moolenaarc024b462019-06-08 18:07:21 +02001486 unchanged(curbuf, TRUE, TRUE);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001487
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001488 bnum = 1; // start with block 1
1489 page_count = 1; // which is 1 page
1490 lnum = 0; // append after line 0 in curbuf
Bram Moolenaar071d4272004-06-13 20:20:40 +00001491 line_count = 0;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001492 idx = 0; // start with first index in block 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00001493 error = 0;
1494 buf->b_ml.ml_stack_top = 0;
1495 buf->b_ml.ml_stack = NULL;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001496 buf->b_ml.ml_stack_size = 0; // no stack yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00001497
1498 if (curbuf->b_ffname == NULL)
1499 cannot_open = TRUE;
1500 else
1501 cannot_open = FALSE;
1502
1503 serious_error = FALSE;
1504 for ( ; !got_int; line_breakcheck())
1505 {
1506 if (hp != NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001507 mf_put(mfp, hp, FALSE, FALSE); // release previous block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001508
1509 /*
1510 * get block
1511 */
1512 if ((hp = mf_get(mfp, (blocknr_T)bnum, page_count)) == NULL)
1513 {
1514 if (bnum == 1)
1515 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001516 semsg(_("E309: Unable to read block 1 from %s"), mfp->mf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001517 goto theend;
1518 }
1519 ++error;
1520 ml_append(lnum++, (char_u *)_("???MANY LINES MISSING"),
1521 (colnr_T)0, TRUE);
1522 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001523 else // there is a block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001524 {
1525 pp = (PTR_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001526 if (pp->pb_id == PTR_ID) // it is a pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001527 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001528 // check line count when using pointer block first time
Bram Moolenaar071d4272004-06-13 20:20:40 +00001529 if (idx == 0 && line_count != 0)
1530 {
1531 for (i = 0; i < (int)pp->pb_count; ++i)
1532 line_count -= pp->pb_pointer[i].pe_line_count;
1533 if (line_count != 0)
1534 {
1535 ++error;
1536 ml_append(lnum++, (char_u *)_("???LINE COUNT WRONG"),
1537 (colnr_T)0, TRUE);
1538 }
1539 }
1540
1541 if (pp->pb_count == 0)
1542 {
1543 ml_append(lnum++, (char_u *)_("???EMPTY BLOCK"),
1544 (colnr_T)0, TRUE);
1545 ++error;
1546 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001547 else if (idx < (int)pp->pb_count) // go a block deeper
Bram Moolenaar071d4272004-06-13 20:20:40 +00001548 {
1549 if (pp->pb_pointer[idx].pe_bnum < 0)
1550 {
1551 /*
1552 * Data block with negative block number.
1553 * Try to read lines from the original file.
1554 * This is slow, but it works.
1555 */
1556 if (!cannot_open)
1557 {
1558 line_count = pp->pb_pointer[idx].pe_line_count;
1559 if (readfile(curbuf->b_ffname, NULL, lnum,
1560 pp->pb_pointer[idx].pe_old_lnum - 1,
Bram Moolenaare13b9af2017-01-13 22:01:02 +01001561 line_count, NULL, 0) != OK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001562 cannot_open = TRUE;
1563 else
1564 lnum += line_count;
1565 }
1566 if (cannot_open)
1567 {
1568 ++error;
1569 ml_append(lnum++, (char_u *)_("???LINES MISSING"),
1570 (colnr_T)0, TRUE);
1571 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001572 ++idx; // get same block again for next index
Bram Moolenaar071d4272004-06-13 20:20:40 +00001573 continue;
1574 }
1575
1576 /*
1577 * going one block deeper in the tree
1578 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001579 if ((top = ml_add_stack(buf)) < 0) // new entry in stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00001580 {
1581 ++error;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001582 break; // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00001583 }
1584 ip = &(buf->b_ml.ml_stack[top]);
1585 ip->ip_bnum = bnum;
1586 ip->ip_index = idx;
1587
1588 bnum = pp->pb_pointer[idx].pe_bnum;
1589 line_count = pp->pb_pointer[idx].pe_line_count;
1590 page_count = pp->pb_pointer[idx].pe_page_count;
Bram Moolenaar986a0032011-06-13 01:07:27 +02001591 idx = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001592 continue;
1593 }
1594 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001595 else // not a pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001596 {
1597 dp = (DATA_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001598 if (dp->db_id != DATA_ID) // block id wrong
Bram Moolenaar071d4272004-06-13 20:20:40 +00001599 {
1600 if (bnum == 1)
1601 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001602 semsg(_("E310: Block 1 ID wrong (%s not a .swp file?)"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001603 mfp->mf_fname);
1604 goto theend;
1605 }
1606 ++error;
1607 ml_append(lnum++, (char_u *)_("???BLOCK MISSING"),
1608 (colnr_T)0, TRUE);
1609 }
1610 else
1611 {
1612 /*
1613 * it is a data block
1614 * Append all the lines in this block
1615 */
1616 has_error = FALSE;
1617 /*
1618 * check length of block
1619 * if wrong, use length in pointer block
1620 */
1621 if (page_count * mfp->mf_page_size != dp->db_txt_end)
1622 {
1623 ml_append(lnum++, (char_u *)_("??? from here until ???END lines may be messed up"),
1624 (colnr_T)0, TRUE);
1625 ++error;
1626 has_error = TRUE;
1627 dp->db_txt_end = page_count * mfp->mf_page_size;
1628 }
1629
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001630 // make sure there is a NUL at the end of the block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001631 *((char_u *)dp + dp->db_txt_end - 1) = NUL;
1632
1633 /*
1634 * check number of lines in block
1635 * if wrong, use count in data block
1636 */
1637 if (line_count != dp->db_line_count)
1638 {
1639 ml_append(lnum++, (char_u *)_("??? from here until ???END lines may have been inserted/deleted"),
1640 (colnr_T)0, TRUE);
1641 ++error;
1642 has_error = TRUE;
1643 }
1644
1645 for (i = 0; i < dp->db_line_count; ++i)
1646 {
1647 txt_start = (dp->db_index[i] & DB_INDEX_MASK);
Bram Moolenaar740885b2009-11-03 14:33:17 +00001648 if (txt_start <= (int)HEADER_SIZE
Bram Moolenaar071d4272004-06-13 20:20:40 +00001649 || txt_start >= (int)dp->db_txt_end)
1650 {
1651 p = (char_u *)"???";
1652 ++error;
1653 }
1654 else
1655 p = (char_u *)dp + txt_start;
1656 ml_append(lnum++, p, (colnr_T)0, TRUE);
1657 }
1658 if (has_error)
Bram Moolenaar740885b2009-11-03 14:33:17 +00001659 ml_append(lnum++, (char_u *)_("???END"),
1660 (colnr_T)0, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001661 }
1662 }
1663 }
1664
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001665 if (buf->b_ml.ml_stack_top == 0) // finished
Bram Moolenaar071d4272004-06-13 20:20:40 +00001666 break;
1667
1668 /*
1669 * go one block up in the tree
1670 */
1671 ip = &(buf->b_ml.ml_stack[--(buf->b_ml.ml_stack_top)]);
1672 bnum = ip->ip_bnum;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001673 idx = ip->ip_index + 1; // go to next index
Bram Moolenaar071d4272004-06-13 20:20:40 +00001674 page_count = 1;
1675 }
1676
1677 /*
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001678 * Compare the buffer contents with the original file. When they differ
1679 * set the 'modified' flag.
1680 * Lines 1 - lnum are the new contents.
1681 * Lines lnum + 1 to ml_line_count are the original contents.
1682 * Line ml_line_count + 1 in the dummy empty line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001683 */
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001684 if (orig_file_status != OK || curbuf->b_ml.ml_line_count != lnum * 2 + 1)
1685 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001686 // Recovering an empty file results in two lines and the first line is
1687 // empty. Don't set the modified flag then.
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001688 if (!(curbuf->b_ml.ml_line_count == 2 && *ml_get(1) == NUL))
1689 {
Bram Moolenaarec28d152019-05-11 18:36:34 +02001690 changed_internal();
Bram Moolenaar95c526e2017-02-25 14:59:34 +01001691 ++CHANGEDTICK(curbuf);
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001692 }
1693 }
1694 else
1695 {
1696 for (idx = 1; idx <= lnum; ++idx)
1697 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001698 // Need to copy one line, fetching the other one may flush it.
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001699 p = vim_strsave(ml_get(idx));
1700 i = STRCMP(p, ml_get(idx + lnum));
1701 vim_free(p);
1702 if (i != 0)
1703 {
Bram Moolenaarec28d152019-05-11 18:36:34 +02001704 changed_internal();
Bram Moolenaar95c526e2017-02-25 14:59:34 +01001705 ++CHANGEDTICK(curbuf);
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001706 break;
1707 }
1708 }
1709 }
1710
1711 /*
1712 * Delete the lines from the original file and the dummy line from the
1713 * empty buffer. These will now be after the last line in the buffer.
1714 */
1715 while (curbuf->b_ml.ml_line_count > lnum
1716 && !(curbuf->b_ml.ml_flags & ML_EMPTY))
Bram Moolenaarca70c072020-05-30 20:30:46 +02001717 ml_delete(curbuf->b_ml.ml_line_count);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001718 curbuf->b_flags |= BF_RECOVERED;
Bram Moolenaare3f50ad2021-06-09 12:33:40 +02001719 check_cursor();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001720
1721 recoverymode = FALSE;
1722 if (got_int)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001723 emsg(_("E311: Recovery Interrupted"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001724 else if (error)
1725 {
1726 ++no_wait_return;
Bram Moolenaar32526b32019-01-19 17:43:09 +01001727 msg(">>>>>>>>>>>>>");
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001728 emsg(_("E312: Errors detected while recovering; look for lines starting with ???"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001729 --no_wait_return;
Bram Moolenaar32526b32019-01-19 17:43:09 +01001730 msg(_("See \":help E312\" for more information."));
1731 msg(">>>>>>>>>>>>>");
Bram Moolenaar071d4272004-06-13 20:20:40 +00001732 }
1733 else
1734 {
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001735 if (curbuf->b_changed)
1736 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001737 msg(_("Recovery completed. You should check if everything is OK."));
1738 msg_puts(_("\n(You might want to write out this file under another name\n"));
1739 msg_puts(_("and run diff with the original file to check for changes)"));
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001740 }
1741 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001742 msg(_("Recovery completed. Buffer contents equals file contents."));
Bram Moolenaarf8835082020-11-09 21:04:17 +01001743 msg_puts(_("\nYou may want to delete the .swp file now."));
1744#if defined(UNIX) || defined(MSWIN)
Bram Moolenaarf52f0602021-03-10 21:26:37 +01001745 if (swapfile_process_running(b0p, fname_used))
Bram Moolenaarf8835082020-11-09 21:04:17 +01001746 {
1747 // Warn there could be an active Vim on the same file, the user may
1748 // want to kill it.
1749 msg_puts(_("\nNote: process STILL RUNNING: "));
1750 msg_outnum(char_to_long(b0p->b0_pid));
1751 }
1752#endif
1753 msg_puts("\n\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00001754 cmdline_row = msg_row;
1755 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001756#ifdef FEAT_CRYPT
1757 if (*buf->b_p_key != NUL && STRCMP(curbuf->b_p_key, buf->b_p_key) != 0)
1758 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001759 msg_puts(_("Using crypt key from swap file for the text file.\n"));
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001760 set_option_value((char_u *)"key", 0L, buf->b_p_key, OPT_LOCAL);
1761 }
1762#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001763 redraw_curbuf_later(NOT_VALID);
1764
1765theend:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001766 vim_free(fname_used);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001767 recoverymode = FALSE;
1768 if (mfp != NULL)
1769 {
1770 if (hp != NULL)
1771 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001772 mf_close(mfp, FALSE); // will also vim_free(mfp->mf_fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001773 }
Bram Moolenaardf88dda2007-01-09 13:34:50 +00001774 if (buf != NULL)
1775 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001776#ifdef FEAT_CRYPT
1777 if (buf->b_p_key != curbuf->b_p_key)
1778 free_string_option(buf->b_p_key);
Bram Moolenaar0ad014c2010-07-25 14:00:46 +02001779 free_string_option(buf->b_p_cm);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001780#endif
Bram Moolenaardf88dda2007-01-09 13:34:50 +00001781 vim_free(buf->b_ml.ml_stack);
1782 vim_free(buf);
1783 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001784 if (serious_error && called_from_main)
1785 ml_close(curbuf, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001786 else
1787 {
1788 apply_autocmds(EVENT_BUFREADPOST, NULL, curbuf->b_fname, FALSE, curbuf);
1789 apply_autocmds(EVENT_BUFWINENTER, NULL, curbuf->b_fname, FALSE, curbuf);
1790 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001791 return;
1792}
1793
1794/*
1795 * Find the names of swap files in current directory and the directory given
1796 * with the 'directory' option.
1797 *
1798 * Used to:
1799 * - list the swap files for "vim -r"
1800 * - count the number of swap files when recovering
1801 * - list the swap files when recovering
1802 * - find the name of the n'th swap file when recovering
1803 */
1804 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001805recover_names(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001806 char_u *fname, // base for swap file name
1807 int list, // when TRUE, list the swap file names
1808 int nr, // when non-zero, return nr'th swap file name
1809 char_u **fname_out) // result when "nr" > 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001810{
1811 int num_names;
1812 char_u *(names[6]);
1813 char_u *tail;
1814 char_u *p;
1815 int num_files;
1816 int file_count = 0;
1817 char_u **files;
1818 int i;
1819 char_u *dirp;
1820 char_u *dir_name;
Bram Moolenaar64354da2010-05-25 21:37:17 +02001821 char_u *fname_res = NULL;
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001822#ifdef HAVE_READLINK
1823 char_u fname_buf[MAXPATHL];
Bram Moolenaar64354da2010-05-25 21:37:17 +02001824#endif
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001825
Bram Moolenaar64354da2010-05-25 21:37:17 +02001826 if (fname != NULL)
1827 {
1828#ifdef HAVE_READLINK
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001829 // Expand symlink in the file name, because the swap file is created
1830 // with the actual file instead of with the symlink.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001831 if (resolve_symlink(fname, fname_buf) == OK)
1832 fname_res = fname_buf;
1833 else
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001834#endif
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001835 fname_res = fname;
Bram Moolenaar64354da2010-05-25 21:37:17 +02001836 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001837
1838 if (list)
1839 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001840 // use msg() to start the scrolling properly
Bram Moolenaar32526b32019-01-19 17:43:09 +01001841 msg(_("Swap files found:"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001842 msg_putchar('\n');
1843 }
1844
1845 /*
1846 * Do the loop for every directory in 'directory'.
1847 * First allocate some memory to put the directory name in.
1848 */
Bram Moolenaar964b3742019-05-24 18:54:09 +02001849 dir_name = alloc(STRLEN(p_dir) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001850 dirp = p_dir;
1851 while (dir_name != NULL && *dirp)
1852 {
1853 /*
1854 * Isolate a directory name from *dirp and put it in dir_name (we know
1855 * it is large enough, so use 31000 for length).
1856 * Advance dirp to next directory name.
1857 */
1858 (void)copy_option_part(&dirp, dir_name, 31000, ",");
1859
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001860 if (dir_name[0] == '.' && dir_name[1] == NUL) // check current dir
Bram Moolenaar071d4272004-06-13 20:20:40 +00001861 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001862 if (fname == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001863 {
1864#ifdef VMS
1865 names[0] = vim_strsave((char_u *)"*_sw%");
1866#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001867 names[0] = vim_strsave((char_u *)"*.sw?");
Bram Moolenaar071d4272004-06-13 20:20:40 +00001868#endif
Bram Moolenaar4f974752019-02-17 17:44:42 +01001869#if defined(UNIX) || defined(MSWIN)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001870 // For Unix names starting with a dot are special. MS-Windows
1871 // supports this too, on some file systems.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001872 names[1] = vim_strsave((char_u *)".*.sw?");
1873 names[2] = vim_strsave((char_u *)".sw?");
1874 num_names = 3;
1875#else
1876# ifdef VMS
1877 names[1] = vim_strsave((char_u *)".*_sw%");
1878 num_names = 2;
1879# else
1880 num_names = 1;
1881# endif
1882#endif
1883 }
1884 else
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001885 num_names = recov_file_names(names, fname_res, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001886 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001887 else // check directory dir_name
Bram Moolenaar071d4272004-06-13 20:20:40 +00001888 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001889 if (fname == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001890 {
1891#ifdef VMS
1892 names[0] = concat_fnames(dir_name, (char_u *)"*_sw%", TRUE);
1893#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001894 names[0] = concat_fnames(dir_name, (char_u *)"*.sw?", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001895#endif
Bram Moolenaar4f974752019-02-17 17:44:42 +01001896#if defined(UNIX) || defined(MSWIN)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001897 // For Unix names starting with a dot are special. MS-Windows
1898 // supports this too, on some file systems.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001899 names[1] = concat_fnames(dir_name, (char_u *)".*.sw?", TRUE);
1900 names[2] = concat_fnames(dir_name, (char_u *)".sw?", TRUE);
1901 num_names = 3;
1902#else
1903# ifdef VMS
1904 names[1] = concat_fnames(dir_name, (char_u *)".*_sw%", TRUE);
1905 num_names = 2;
1906# else
1907 num_names = 1;
1908# endif
1909#endif
1910 }
1911 else
1912 {
Bram Moolenaar4f974752019-02-17 17:44:42 +01001913#if defined(UNIX) || defined(MSWIN)
Bram Moolenaarb113c3a2017-02-28 21:26:17 +01001914 int len = (int)STRLEN(dir_name);
Bram Moolenaarc525e3a2017-02-18 16:59:02 +01001915
1916 p = dir_name + len;
1917 if (after_pathsep(dir_name, p) && len > 1 && p[-1] == p[-2])
Bram Moolenaar071d4272004-06-13 20:20:40 +00001918 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001919 // Ends with '//', Use Full path for swap name
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001920 tail = make_percent_swname(dir_name, fname_res);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001921 }
1922 else
1923#endif
1924 {
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001925 tail = gettail(fname_res);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001926 tail = concat_fnames(dir_name, tail, TRUE);
1927 }
1928 if (tail == NULL)
1929 num_names = 0;
1930 else
1931 {
1932 num_names = recov_file_names(names, tail, FALSE);
1933 vim_free(tail);
1934 }
1935 }
1936 }
1937
Bram Moolenaar0d3cb732019-05-18 13:05:18 +02001938 // check for out-of-memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00001939 for (i = 0; i < num_names; ++i)
1940 {
1941 if (names[i] == NULL)
1942 {
1943 for (i = 0; i < num_names; ++i)
1944 vim_free(names[i]);
1945 num_names = 0;
1946 }
1947 }
1948 if (num_names == 0)
1949 num_files = 0;
1950 else if (expand_wildcards(num_names, names, &num_files, &files,
Bram Moolenaar99499b12019-05-23 21:35:48 +02001951 EW_NOTENV|EW_KEEPALL|EW_FILE|EW_SILENT) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001952 num_files = 0;
1953
1954 /*
1955 * When no swap file found, wildcard expansion might have failed (e.g.
1956 * not able to execute the shell).
1957 * Try finding a swap file by simply adding ".swp" to the file name.
1958 */
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001959 if (*dirp == NUL && file_count + num_files == 0 && fname != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001960 {
Bram Moolenaar8767f522016-07-01 17:17:39 +02001961 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001962 char_u *swapname;
1963
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001964 swapname = modname(fname_res,
Bram Moolenaare60acc12011-05-10 16:41:25 +02001965#if defined(VMS)
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001966 (char_u *)"_swp", FALSE
Bram Moolenaar071d4272004-06-13 20:20:40 +00001967#else
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001968 (char_u *)".swp", TRUE
Bram Moolenaar071d4272004-06-13 20:20:40 +00001969#endif
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001970 );
Bram Moolenaar071d4272004-06-13 20:20:40 +00001971 if (swapname != NULL)
1972 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001973 if (mch_stat((char *)swapname, &st) != -1) // It exists!
Bram Moolenaar071d4272004-06-13 20:20:40 +00001974 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001975 files = ALLOC_ONE(char_u *);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001976 if (files != NULL)
1977 {
1978 files[0] = swapname;
1979 swapname = NULL;
1980 num_files = 1;
1981 }
1982 }
1983 vim_free(swapname);
1984 }
1985 }
1986
1987 /*
1988 * remove swapfile name of the current buffer, it must be ignored
1989 */
1990 if (curbuf->b_ml.ml_mfp != NULL
1991 && (p = curbuf->b_ml.ml_mfp->mf_fname) != NULL)
1992 {
1993 for (i = 0; i < num_files; ++i)
Bram Moolenaar99499b12019-05-23 21:35:48 +02001994 // Do not expand wildcards, on windows would try to expand
1995 // "%tmp%" in "%tmp%file".
1996 if (fullpathcmp(p, files[i], TRUE, FALSE) & FPC_SAME)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001997 {
Bram Moolenaar99499b12019-05-23 21:35:48 +02001998 // Remove the name from files[i]. Move further entries
1999 // down. When the array becomes empty free it here, since
2000 // FreeWild() won't be called below.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002001 vim_free(files[i]);
Bram Moolenaar9439cdd2009-04-22 13:39:36 +00002002 if (--num_files == 0)
2003 vim_free(files);
2004 else
2005 for ( ; i < num_files; ++i)
2006 files[i] = files[i + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002007 }
2008 }
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00002009 if (nr > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002010 {
2011 file_count += num_files;
2012 if (nr <= file_count)
2013 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002014 *fname_out = vim_strsave(
2015 files[nr - 1 + num_files - file_count]);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002016 dirp = (char_u *)""; // stop searching
Bram Moolenaar071d4272004-06-13 20:20:40 +00002017 }
2018 }
2019 else if (list)
2020 {
2021 if (dir_name[0] == '.' && dir_name[1] == NUL)
2022 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002023 if (fname == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002024 msg_puts(_(" In current directory:\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002025 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002026 msg_puts(_(" Using specified name:\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002027 }
2028 else
2029 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002030 msg_puts(_(" In directory "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002031 msg_home_replace(dir_name);
Bram Moolenaar32526b32019-01-19 17:43:09 +01002032 msg_puts(":\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00002033 }
2034
2035 if (num_files)
2036 {
2037 for (i = 0; i < num_files; ++i)
2038 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002039 // print the swap file name
Bram Moolenaar071d4272004-06-13 20:20:40 +00002040 msg_outnum((long)++file_count);
Bram Moolenaar32526b32019-01-19 17:43:09 +01002041 msg_puts(". ");
2042 msg_puts((char *)gettail(files[i]));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002043 msg_putchar('\n');
2044 (void)swapfile_info(files[i]);
2045 }
2046 }
2047 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002048 msg_puts(_(" -- none --\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002049 out_flush();
2050 }
2051 else
2052 file_count += num_files;
2053
2054 for (i = 0; i < num_names; ++i)
2055 vim_free(names[i]);
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00002056 if (num_files > 0)
2057 FreeWild(num_files, files);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002058 }
2059 vim_free(dir_name);
2060 return file_count;
2061}
2062
Bram Moolenaar4f974752019-02-17 17:44:42 +01002063#if defined(UNIX) || defined(MSWIN) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002064/*
Bram Moolenaarb782ba42018-08-07 21:39:28 +02002065 * Need _very_ long file names.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002066 * Append the full path to name with path separators made into percent
2067 * signs, to dir. An unnamed buffer is handled as "" (<currentdir>/"")
2068 */
Bram Moolenaarb782ba42018-08-07 21:39:28 +02002069 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002070make_percent_swname(char_u *dir, char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002071{
Bram Moolenaarb782ba42018-08-07 21:39:28 +02002072 char_u *d = NULL, *s, *f;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002073
Bram Moolenaarb782ba42018-08-07 21:39:28 +02002074 f = fix_fname(name != NULL ? name : (char_u *)"");
Bram Moolenaar071d4272004-06-13 20:20:40 +00002075 if (f != NULL)
2076 {
Bram Moolenaar964b3742019-05-24 18:54:09 +02002077 s = alloc(STRLEN(f) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002078 if (s != NULL)
2079 {
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002080 STRCPY(s, f);
Bram Moolenaar91acfff2017-03-12 19:22:36 +01002081 for (d = s; *d != NUL; MB_PTR_ADV(d))
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002082 if (vim_ispathsep(*d))
2083 *d = '%';
Bram Moolenaar071d4272004-06-13 20:20:40 +00002084 d = concat_fnames(dir, s, TRUE);
2085 vim_free(s);
2086 }
2087 vim_free(f);
2088 }
2089 return d;
2090}
2091#endif
2092
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02002093#if (defined(UNIX) || defined(VMS) || defined(MSWIN)) \
2094 && (defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG))
2095# define HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar071d4272004-06-13 20:20:40 +00002096static int process_still_running;
2097#endif
2098
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002099#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002100/*
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002101 * Return information found in swapfile "fname" in dictionary "d".
2102 * This is used by the swapinfo() function.
2103 */
2104 void
2105get_b0_dict(char_u *fname, dict_T *d)
2106{
2107 int fd;
2108 struct block0 b0;
2109
2110 if ((fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0)) >= 0)
2111 {
2112 if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0))
2113 {
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002114 if (ml_check_b0_id(&b0) == FAIL)
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002115 dict_add_string(d, "error", (char_u *)"Not a swap file");
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002116 else if (b0_magic_wrong(&b0))
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002117 dict_add_string(d, "error", (char_u *)"Magic number mismatch");
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002118 else
2119 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002120 // we have swap information
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002121 dict_add_string_len(d, "version", b0.b0_version, 10);
2122 dict_add_string_len(d, "user", b0.b0_uname, B0_UNAME_SIZE);
2123 dict_add_string_len(d, "host", b0.b0_hname, B0_HNAME_SIZE);
2124 dict_add_string_len(d, "fname", b0.b0_fname, B0_FNAME_SIZE_ORG);
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002125
2126 dict_add_number(d, "pid", char_to_long(b0.b0_pid));
2127 dict_add_number(d, "mtime", char_to_long(b0.b0_mtime));
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002128 dict_add_number(d, "dirty", b0.b0_dirty ? 1 : 0);
2129# ifdef CHECK_INODE
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002130 dict_add_number(d, "inode", char_to_long(b0.b0_ino));
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002131# endif
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002132 }
2133 }
2134 else
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002135 dict_add_string(d, "error", (char_u *)"Cannot read file");
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002136 close(fd);
2137 }
2138 else
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002139 dict_add_string(d, "error", (char_u *)"Cannot open file");
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002140}
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002141#endif
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002142
2143/*
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00002144 * Give information about an existing swap file.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002145 * Returns timestamp (0 when unknown).
2146 */
2147 static time_t
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002148swapfile_info(char_u *fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002149{
Bram Moolenaar8767f522016-07-01 17:17:39 +02002150 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002151 int fd;
2152 struct block0 b0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002153#ifdef UNIX
2154 char_u uname[B0_UNAME_SIZE];
2155#endif
2156
Bram Moolenaar63d25552019-05-10 21:28:38 +02002157 // print the swap file date
Bram Moolenaar071d4272004-06-13 20:20:40 +00002158 if (mch_stat((char *)fname, &st) != -1)
2159 {
2160#ifdef UNIX
Bram Moolenaar63d25552019-05-10 21:28:38 +02002161 // print name of owner of the file
Bram Moolenaar071d4272004-06-13 20:20:40 +00002162 if (mch_get_uname(st.st_uid, uname, B0_UNAME_SIZE) == OK)
2163 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002164 msg_puts(_(" owned by: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002165 msg_outtrans(uname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01002166 msg_puts(_(" dated: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002167 }
2168 else
2169#endif
Bram Moolenaar32526b32019-01-19 17:43:09 +01002170 msg_puts(_(" dated: "));
Bram Moolenaar63d25552019-05-10 21:28:38 +02002171 msg_puts(get_ctime(st.st_mtime, TRUE));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002172 }
Bram Moolenaar63d25552019-05-10 21:28:38 +02002173 else
2174 st.st_mtime = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002175
2176 /*
2177 * print the original file name
2178 */
2179 fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
2180 if (fd >= 0)
2181 {
Bram Moolenaar540fc6f2010-12-17 16:27:16 +01002182 if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002183 {
2184 if (STRNCMP(b0.b0_version, "VIM 3.0", 7) == 0)
2185 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002186 msg_puts(_(" [from Vim version 3.0]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002187 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002188 else if (ml_check_b0_id(&b0) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002189 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002190 msg_puts(_(" [does not look like a Vim swap file]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002191 }
2192 else
2193 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002194 msg_puts(_(" file name: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002195 if (b0.b0_fname[0] == NUL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002196 msg_puts(_("[No Name]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002197 else
2198 msg_outtrans(b0.b0_fname);
2199
Bram Moolenaar32526b32019-01-19 17:43:09 +01002200 msg_puts(_("\n modified: "));
2201 msg_puts(b0.b0_dirty ? _("YES") : _("no"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002202
2203 if (*(b0.b0_uname) != NUL)
2204 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002205 msg_puts(_("\n user name: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002206 msg_outtrans(b0.b0_uname);
2207 }
2208
2209 if (*(b0.b0_hname) != NUL)
2210 {
2211 if (*(b0.b0_uname) != NUL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002212 msg_puts(_(" host name: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002213 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002214 msg_puts(_("\n host name: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002215 msg_outtrans(b0.b0_hname);
2216 }
2217
2218 if (char_to_long(b0.b0_pid) != 0L)
2219 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002220 msg_puts(_("\n process ID: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002221 msg_outnum(char_to_long(b0.b0_pid));
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002222#if defined(UNIX) || defined(MSWIN)
Bram Moolenaarf52f0602021-03-10 21:26:37 +01002223 if (swapfile_process_running(&b0, fname))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002224 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002225 msg_puts(_(" (STILL RUNNING)"));
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02002226# ifdef HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar071d4272004-06-13 20:20:40 +00002227 process_still_running = TRUE;
2228# endif
2229 }
2230#endif
2231 }
2232
2233 if (b0_magic_wrong(&b0))
2234 {
Bram Moolenaar48e330a2016-02-23 14:53:34 +01002235#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002236 if (STRNCMP(b0.b0_hname, "PC ", 3) == 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002237 msg_puts(_("\n [not usable with this version of Vim]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002238 else
2239#endif
Bram Moolenaar32526b32019-01-19 17:43:09 +01002240 msg_puts(_("\n [not usable on this computer]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002241 }
2242 }
2243 }
2244 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002245 msg_puts(_(" [cannot be read]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002246 close(fd);
2247 }
2248 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002249 msg_puts(_(" [cannot be opened]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002250 msg_putchar('\n');
2251
Bram Moolenaar63d25552019-05-10 21:28:38 +02002252 return st.st_mtime;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002253}
2254
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002255/*
2256 * Return TRUE if the swap file looks OK and there are no changes, thus it can
2257 * be safely deleted.
2258 */
2259 static time_t
2260swapfile_unchanged(char_u *fname)
2261{
2262 stat_T st;
2263 int fd;
2264 struct block0 b0;
2265 int ret = TRUE;
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002266
2267 // must be able to stat the swap file
2268 if (mch_stat((char *)fname, &st) == -1)
2269 return FALSE;
2270
2271 // must be able to read the first block
2272 fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
2273 if (fd < 0)
2274 return FALSE;
2275 if (read_eintr(fd, &b0, sizeof(b0)) != sizeof(b0))
2276 {
2277 close(fd);
2278 return FALSE;
2279 }
2280
2281 // the ID and magic number must be correct
2282 if (ml_check_b0_id(&b0) == FAIL|| b0_magic_wrong(&b0))
2283 ret = FALSE;
2284
2285 // must be unchanged
2286 if (b0.b0_dirty)
2287 ret = FALSE;
2288
2289#if defined(UNIX) || defined(MSWIN)
Bram Moolenaarf8835082020-11-09 21:04:17 +01002290 // Host name must be known and must equal the current host name, otherwise
2291 // comparing pid is meaningless.
2292 if (*(b0.b0_hname) == NUL)
2293 {
2294 ret = FALSE;
2295 }
2296 else
2297 {
2298 char_u hostname[B0_HNAME_SIZE];
2299
2300 mch_get_host_name(hostname, B0_HNAME_SIZE);
2301 hostname[B0_HNAME_SIZE - 1] = NUL;
Bram Moolenaare79cdb62020-11-21 13:51:16 +01002302 b0.b0_hname[B0_HNAME_SIZE - 1] = NUL; // in case of corruption
Bram Moolenaarf8835082020-11-09 21:04:17 +01002303 if (STRICMP(b0.b0_hname, hostname) != 0)
2304 ret = FALSE;
2305 }
2306
Bram Moolenaar32aa1022019-11-02 22:54:41 +01002307 // process must be known and not be running
Bram Moolenaarf52f0602021-03-10 21:26:37 +01002308 if (char_to_long(b0.b0_pid) == 0L || swapfile_process_running(&b0, fname))
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002309 ret = FALSE;
2310#endif
2311
Bram Moolenaarf8835082020-11-09 21:04:17 +01002312 // We do not check the user, it should be irrelevant for whether the swap
2313 // file is still useful.
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002314
2315 close(fd);
2316 return ret;
2317}
2318
Bram Moolenaar071d4272004-06-13 20:20:40 +00002319 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002320recov_file_names(char_u **names, char_u *path, int prepend_dot)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002321{
2322 int num_names;
2323
Bram Moolenaar071d4272004-06-13 20:20:40 +00002324 /*
2325 * (Win32 and Win64) never short names, but do prepend a dot.
2326 * (Not MS-DOS or Win32 or Win64) maybe short name, maybe not: Try both.
2327 * Only use the short name if it is different.
2328 */
2329 char_u *p;
2330 int i;
Bram Moolenaar4f974752019-02-17 17:44:42 +01002331# ifndef MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002332 int shortname = curbuf->b_shortname;
2333
2334 curbuf->b_shortname = FALSE;
2335# endif
2336
2337 num_names = 0;
2338
2339 /*
2340 * May also add the file name with a dot prepended, for swap file in same
2341 * dir as original file.
2342 */
2343 if (prepend_dot)
2344 {
2345 names[num_names] = modname(path, (char_u *)".sw?", TRUE);
2346 if (names[num_names] == NULL)
2347 goto end;
2348 ++num_names;
2349 }
2350
2351 /*
2352 * Form the normal swap file name pattern by appending ".sw?".
2353 */
2354#ifdef VMS
2355 names[num_names] = concat_fnames(path, (char_u *)"_sw%", FALSE);
2356#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002357 names[num_names] = concat_fnames(path, (char_u *)".sw?", FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002358#endif
2359 if (names[num_names] == NULL)
2360 goto end;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002361 if (num_names >= 1) // check if we have the same name twice
Bram Moolenaar071d4272004-06-13 20:20:40 +00002362 {
2363 p = names[num_names - 1];
2364 i = (int)STRLEN(names[num_names - 1]) - (int)STRLEN(names[num_names]);
2365 if (i > 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002366 p += i; // file name has been expanded to full path
Bram Moolenaar071d4272004-06-13 20:20:40 +00002367
2368 if (STRCMP(p, names[num_names]) != 0)
2369 ++num_names;
2370 else
2371 vim_free(names[num_names]);
2372 }
2373 else
2374 ++num_names;
2375
Bram Moolenaar4f974752019-02-17 17:44:42 +01002376# ifndef MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002377 /*
2378 * Also try with 'shortname' set, in case the file is on a DOS filesystem.
2379 */
2380 curbuf->b_shortname = TRUE;
2381#ifdef VMS
2382 names[num_names] = modname(path, (char_u *)"_sw%", FALSE);
2383#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002384 names[num_names] = modname(path, (char_u *)".sw?", FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002385#endif
2386 if (names[num_names] == NULL)
2387 goto end;
2388
2389 /*
2390 * Remove the one from 'shortname', if it's the same as with 'noshortname'.
2391 */
2392 p = names[num_names];
2393 i = STRLEN(names[num_names]) - STRLEN(names[num_names - 1]);
2394 if (i > 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002395 p += i; // file name has been expanded to full path
Bram Moolenaar071d4272004-06-13 20:20:40 +00002396 if (STRCMP(names[num_names - 1], p) == 0)
2397 vim_free(names[num_names]);
2398 else
2399 ++num_names;
2400# endif
2401
2402end:
Bram Moolenaar4f974752019-02-17 17:44:42 +01002403# ifndef MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002404 curbuf->b_shortname = shortname;
2405# endif
2406
Bram Moolenaar071d4272004-06-13 20:20:40 +00002407 return num_names;
2408}
2409
2410/*
2411 * sync all memlines
2412 *
2413 * If 'check_file' is TRUE, check if original file exists and was not changed.
2414 * If 'check_char' is TRUE, stop syncing when character becomes available, but
2415 * always sync at least one block.
2416 */
2417 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002418ml_sync_all(int check_file, int check_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002419{
2420 buf_T *buf;
Bram Moolenaar8767f522016-07-01 17:17:39 +02002421 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002422
Bram Moolenaar29323592016-07-24 22:04:11 +02002423 FOR_ALL_BUFFERS(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002424 {
Christian Brabandtf573c6e2021-06-20 14:02:16 +02002425 if (buf->b_ml.ml_mfp == NULL
2426 || buf->b_ml.ml_mfp->mf_fname == NULL
2427 || buf->b_ml.ml_mfp->mf_fd < 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002428 continue; // no file
Bram Moolenaar071d4272004-06-13 20:20:40 +00002429
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002430 ml_flush_line(buf); // flush buffered line
2431 // flush locked block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002432 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH);
2433 if (bufIsChanged(buf) && check_file && mf_need_trans(buf->b_ml.ml_mfp)
2434 && buf->b_ffname != NULL)
2435 {
2436 /*
2437 * If the original file does not exist anymore or has been changed
2438 * call ml_preserve() to get rid of all negative numbered blocks.
2439 */
2440 if (mch_stat((char *)buf->b_ffname, &st) == -1
2441 || st.st_mtime != buf->b_mtime_read
Bram Moolenaar914703b2010-05-31 21:59:46 +02002442 || st.st_size != buf->b_orig_size)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002443 {
2444 ml_preserve(buf, FALSE);
2445 did_check_timestamps = FALSE;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002446 need_check_timestamps = TRUE; // give message later
Bram Moolenaar071d4272004-06-13 20:20:40 +00002447 }
2448 }
2449 if (buf->b_ml.ml_mfp->mf_dirty)
2450 {
2451 (void)mf_sync(buf->b_ml.ml_mfp, (check_char ? MFS_STOP : 0)
2452 | (bufIsChanged(buf) ? MFS_FLUSH : 0));
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002453 if (check_char && ui_char_avail()) // character available now
Bram Moolenaar071d4272004-06-13 20:20:40 +00002454 break;
2455 }
2456 }
2457}
2458
2459/*
2460 * sync one buffer, including negative blocks
2461 *
2462 * after this all the blocks are in the swap file
2463 *
2464 * Used for the :preserve command and when the original file has been
2465 * changed or deleted.
2466 *
2467 * when message is TRUE the success of preserving is reported
2468 */
2469 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002470ml_preserve(buf_T *buf, int message)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002471{
2472 bhdr_T *hp;
2473 linenr_T lnum;
2474 memfile_T *mfp = buf->b_ml.ml_mfp;
2475 int status;
2476 int got_int_save = got_int;
2477
2478 if (mfp == NULL || mfp->mf_fname == NULL)
2479 {
2480 if (message)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002481 emsg(_("E313: Cannot preserve, there is no swap file"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002482 return;
2483 }
2484
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002485 // We only want to stop when interrupted here, not when interrupted
2486 // before.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002487 got_int = FALSE;
2488
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002489 ml_flush_line(buf); // flush buffered line
2490 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush locked block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002491 status = mf_sync(mfp, MFS_ALL | MFS_FLUSH);
2492
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002493 // stack is invalid after mf_sync(.., MFS_ALL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002494 buf->b_ml.ml_stack_top = 0;
2495
2496 /*
2497 * Some of the data blocks may have been changed from negative to
2498 * positive block number. In that case the pointer blocks need to be
2499 * updated.
2500 *
2501 * We don't know in which pointer block the references are, so we visit
2502 * all data blocks until there are no more translations to be done (or
2503 * we hit the end of the file, which can only happen in case a write fails,
2504 * e.g. when file system if full).
2505 * ml_find_line() does the work by translating the negative block numbers
2506 * when getting the first line of each data block.
2507 */
2508 if (mf_need_trans(mfp) && !got_int)
2509 {
2510 lnum = 1;
2511 while (mf_need_trans(mfp) && lnum <= buf->b_ml.ml_line_count)
2512 {
2513 hp = ml_find_line(buf, lnum, ML_FIND);
2514 if (hp == NULL)
2515 {
2516 status = FAIL;
2517 goto theend;
2518 }
2519 CHECK(buf->b_ml.ml_locked_low != lnum, "low != lnum");
2520 lnum = buf->b_ml.ml_locked_high + 1;
2521 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002522 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush locked block
2523 // sync the updated pointer blocks
Bram Moolenaar071d4272004-06-13 20:20:40 +00002524 if (mf_sync(mfp, MFS_ALL | MFS_FLUSH) == FAIL)
2525 status = FAIL;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002526 buf->b_ml.ml_stack_top = 0; // stack is invalid now
Bram Moolenaar071d4272004-06-13 20:20:40 +00002527 }
2528theend:
2529 got_int |= got_int_save;
2530
2531 if (message)
2532 {
2533 if (status == OK)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002534 msg(_("File preserved"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002535 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002536 emsg(_("E314: Preserve failed"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002537 }
2538}
2539
2540/*
2541 * NOTE: The pointer returned by the ml_get_*() functions only remains valid
2542 * until the next call!
2543 * line1 = ml_get(1);
2544 * line2 = ml_get(2); // line1 is now invalid!
2545 * Make a copy of the line if necessary.
2546 */
2547/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002548 * Return a pointer to a (read-only copy of a) line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002549 *
2550 * On failure an error message is given and IObuff is returned (to avoid
2551 * having to check for error everywhere).
2552 */
2553 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002554ml_get(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002555{
2556 return ml_get_buf(curbuf, lnum, FALSE);
2557}
2558
2559/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002560 * Return pointer to position "pos".
Bram Moolenaar071d4272004-06-13 20:20:40 +00002561 */
2562 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002563ml_get_pos(pos_T *pos)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002564{
2565 return (ml_get_buf(curbuf, pos->lnum, FALSE) + pos->col);
2566}
2567
2568/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002569 * Return pointer to cursor line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002570 */
2571 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002572ml_get_curline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002573{
2574 return ml_get_buf(curbuf, curwin->w_cursor.lnum, FALSE);
2575}
2576
2577/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002578 * Return pointer to cursor position.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002579 */
2580 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002581ml_get_cursor(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002582{
2583 return (ml_get_buf(curbuf, curwin->w_cursor.lnum, FALSE) +
2584 curwin->w_cursor.col);
2585}
2586
2587/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002588 * Return a pointer to a line in a specific buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +00002589 *
2590 * "will_change": if TRUE mark the buffer dirty (chars in the line will be
2591 * changed)
2592 */
2593 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002594ml_get_buf(
2595 buf_T *buf,
2596 linenr_T lnum,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002597 int will_change) // line will be changed
Bram Moolenaar071d4272004-06-13 20:20:40 +00002598{
Bram Moolenaarad40f022007-02-13 03:01:39 +00002599 bhdr_T *hp;
2600 DATA_BL *dp;
Bram Moolenaarad40f022007-02-13 03:01:39 +00002601 static int recursive = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002602
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002603 if (lnum > buf->b_ml.ml_line_count) // invalid line number
Bram Moolenaar071d4272004-06-13 20:20:40 +00002604 {
Bram Moolenaarad40f022007-02-13 03:01:39 +00002605 if (recursive == 0)
2606 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002607 // Avoid giving this message for a recursive call, may happen when
2608 // the GUI redraws part of the text.
Bram Moolenaarad40f022007-02-13 03:01:39 +00002609 ++recursive;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002610 siemsg(_("E315: ml_get: invalid lnum: %ld"), lnum);
Bram Moolenaarad40f022007-02-13 03:01:39 +00002611 --recursive;
2612 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002613errorret:
2614 STRCPY(IObuff, "???");
Bram Moolenaaradfde112019-05-25 22:11:45 +02002615 buf->b_ml.ml_line_len = 4;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002616 return IObuff;
2617 }
Bram Moolenaaradfde112019-05-25 22:11:45 +02002618 if (lnum <= 0) // pretend line 0 is line 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00002619 lnum = 1;
2620
Bram Moolenaaradfde112019-05-25 22:11:45 +02002621 if (buf->b_ml.ml_mfp == NULL) // there are no lines
2622 {
2623 buf->b_ml.ml_line_len = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002624 return (char_u *)"";
Bram Moolenaaradfde112019-05-25 22:11:45 +02002625 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002626
Bram Moolenaar37d619f2010-03-10 14:46:26 +01002627 /*
2628 * See if it is the same line as requested last time.
2629 * Otherwise may need to flush last used line.
2630 * Don't use the last used line when 'swapfile' is reset, need to load all
2631 * blocks.
2632 */
Bram Moolenaar47b8b152007-02-07 02:41:57 +00002633 if (buf->b_ml.ml_line_lnum != lnum || mf_dont_release)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002634 {
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002635 unsigned start, end;
2636 colnr_T len;
2637 int idx;
2638
Bram Moolenaar071d4272004-06-13 20:20:40 +00002639 ml_flush_line(buf);
2640
2641 /*
2642 * Find the data block containing the line.
2643 * This also fills the stack with the blocks from the root to the data
2644 * block and releases any locked block.
2645 */
2646 if ((hp = ml_find_line(buf, lnum, ML_FIND)) == NULL)
2647 {
Bram Moolenaarad40f022007-02-13 03:01:39 +00002648 if (recursive == 0)
2649 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002650 // Avoid giving this message for a recursive call, may happen
2651 // when the GUI redraws part of the text.
Bram Moolenaarad40f022007-02-13 03:01:39 +00002652 ++recursive;
Bram Moolenaarcb868932019-10-26 20:56:21 +02002653 get_trans_bufname(buf);
2654 shorten_dir(NameBuff);
2655 siemsg(_("E316: ml_get: cannot find line %ld in buffer %d %s"),
2656 lnum, buf->b_fnum, NameBuff);
Bram Moolenaarad40f022007-02-13 03:01:39 +00002657 --recursive;
2658 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002659 goto errorret;
2660 }
2661
2662 dp = (DATA_BL *)(hp->bh_data);
2663
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002664 idx = lnum - buf->b_ml.ml_locked_low;
2665 start = ((dp->db_index[idx]) & DB_INDEX_MASK);
2666 // The text ends where the previous line starts. The first line ends
2667 // at the end of the block.
2668 if (idx == 0)
2669 end = dp->db_txt_end;
2670 else
2671 end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK);
2672 len = end - start;
2673
2674 buf->b_ml.ml_line_ptr = (char_u *)dp + start;
2675 buf->b_ml.ml_line_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002676 buf->b_ml.ml_line_lnum = lnum;
2677 buf->b_ml.ml_flags &= ~ML_LINE_DIRTY;
2678 }
2679 if (will_change)
2680 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
2681
2682 return buf->b_ml.ml_line_ptr;
2683}
2684
2685/*
2686 * Check if a line that was just obtained by a call to ml_get
2687 * is in allocated memory.
2688 */
2689 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002690ml_line_alloced(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002691{
2692 return (curbuf->b_ml.ml_flags & ML_LINE_DIRTY);
2693}
2694
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002695#ifdef FEAT_PROP_POPUP
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002696/*
2697 * Add text properties that continue from the previous line.
2698 */
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002699 static void
2700add_text_props_for_append(
2701 buf_T *buf,
2702 linenr_T lnum,
2703 char_u **line,
2704 int *len,
2705 char_u **tofree)
2706{
2707 int round;
2708 int new_prop_count = 0;
2709 int count;
2710 int n;
2711 char_u *props;
Bram Moolenaarea781452019-09-04 18:53:12 +02002712 int new_len = 0; // init for gcc
Bram Moolenaar8f13d822020-09-12 21:04:23 +02002713 char_u *new_line = NULL;
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002714 textprop_T prop;
2715
2716 // Make two rounds:
2717 // 1. calculate the extra space needed
2718 // 2. allocate the space and fill it
2719 for (round = 1; round <= 2; ++round)
2720 {
2721 if (round == 2)
2722 {
2723 if (new_prop_count == 0)
2724 return; // nothing to do
2725 new_len = *len + new_prop_count * sizeof(textprop_T);
Bram Moolenaar964b3742019-05-24 18:54:09 +02002726 new_line = alloc(new_len);
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002727 if (new_line == NULL)
2728 return;
2729 mch_memmove(new_line, *line, *len);
2730 new_prop_count = 0;
2731 }
2732
2733 // Get the line above to find any props that continue in the next
2734 // line.
2735 count = get_text_props(buf, lnum, &props, FALSE);
2736 for (n = 0; n < count; ++n)
2737 {
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002738 mch_memmove(&prop, props + n * sizeof(textprop_T),
2739 sizeof(textprop_T));
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002740 if (prop.tp_flags & TP_FLAG_CONT_NEXT)
2741 {
2742 if (round == 2)
2743 {
2744 prop.tp_flags |= TP_FLAG_CONT_PREV;
2745 prop.tp_col = 1;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002746 prop.tp_len = *len; // not exactly the right length
2747 mch_memmove(new_line + *len + new_prop_count
2748 * sizeof(textprop_T), &prop, sizeof(textprop_T));
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002749 }
2750 ++new_prop_count;
2751 }
2752 }
2753 }
2754 *line = new_line;
2755 *tofree = new_line;
2756 *len = new_len;
2757}
2758#endif
2759
Bram Moolenaar071d4272004-06-13 20:20:40 +00002760 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002761ml_append_int(
2762 buf_T *buf,
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002763 linenr_T lnum, // append after this line (can be 0)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002764 char_u *line_arg, // text of the new line
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002765 colnr_T len_arg, // length of line, including NUL, or 0
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002766 int flags) // ML_APPEND_ flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00002767{
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002768 char_u *line = line_arg;
2769 colnr_T len = len_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002770 int i;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002771 int line_count; // number of indexes in current block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002772 int offset;
2773 int from, to;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002774 int space_needed; // space needed for new line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002775 int page_size;
2776 int page_count;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002777 int db_idx; // index for lnum in data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002778 bhdr_T *hp;
2779 memfile_T *mfp;
2780 DATA_BL *dp;
2781 PTR_BL *pp;
2782 infoptr_T *ip;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002783#ifdef FEAT_PROP_POPUP
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002784 char_u *tofree = NULL;
2785#endif
2786 int ret = FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002787
Bram Moolenaar071d4272004-06-13 20:20:40 +00002788 if (lnum > buf->b_ml.ml_line_count || buf->b_ml.ml_mfp == NULL)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002789 return FAIL; // lnum out of range
Bram Moolenaar071d4272004-06-13 20:20:40 +00002790
2791 if (lowest_marked && lowest_marked > lnum)
2792 lowest_marked = lnum + 1;
2793
2794 if (len == 0)
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002795 len = (colnr_T)STRLEN(line) + 1; // space needed for the text
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002796
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002797#ifdef FEAT_PROP_POPUP
Bram Moolenaar840f91f2021-05-26 22:32:10 +02002798 if (curbuf->b_has_textprop && lnum > 0
2799 && !(flags & (ML_APPEND_UNDO | ML_APPEND_NOPROP)))
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002800 // Add text properties that continue from the previous line.
2801 add_text_props_for_append(buf, lnum, &line, &len, &tofree);
2802#endif
2803
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002804 space_needed = len + INDEX_SIZE; // space needed for text + index
Bram Moolenaar071d4272004-06-13 20:20:40 +00002805
2806 mfp = buf->b_ml.ml_mfp;
2807 page_size = mfp->mf_page_size;
2808
2809/*
2810 * find the data block containing the previous line
2811 * This also fills the stack with the blocks from the root to the data block
2812 * This also releases any locked block.
2813 */
2814 if ((hp = ml_find_line(buf, lnum == 0 ? (linenr_T)1 : lnum,
2815 ML_INSERT)) == NULL)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002816 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002817
2818 buf->b_ml.ml_flags &= ~ML_EMPTY;
2819
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002820 if (lnum == 0) // got line one instead, correct db_idx
2821 db_idx = -1; // careful, it is negative!
Bram Moolenaar071d4272004-06-13 20:20:40 +00002822 else
2823 db_idx = lnum - buf->b_ml.ml_locked_low;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002824 // get line count before the insertion
Bram Moolenaar071d4272004-06-13 20:20:40 +00002825 line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low;
2826
2827 dp = (DATA_BL *)(hp->bh_data);
2828
2829/*
2830 * If
2831 * - there is not enough room in the current block
2832 * - appending to the last line in the block
2833 * - not appending to the last line in the file
2834 * insert in front of the next block.
2835 */
2836 if ((int)dp->db_free < space_needed && db_idx == line_count - 1
2837 && lnum < buf->b_ml.ml_line_count)
2838 {
2839 /*
2840 * Now that the line is not going to be inserted in the block that we
2841 * expected, the line count has to be adjusted in the pointer blocks
2842 * by using ml_locked_lineadd.
2843 */
2844 --(buf->b_ml.ml_locked_lineadd);
2845 --(buf->b_ml.ml_locked_high);
2846 if ((hp = ml_find_line(buf, lnum + 1, ML_INSERT)) == NULL)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002847 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002848
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002849 db_idx = -1; // careful, it is negative!
2850 // get line count before the insertion
Bram Moolenaar071d4272004-06-13 20:20:40 +00002851 line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low;
2852 CHECK(buf->b_ml.ml_locked_low != lnum + 1, "locked_low != lnum + 1");
2853
2854 dp = (DATA_BL *)(hp->bh_data);
2855 }
2856
2857 ++buf->b_ml.ml_line_count;
2858
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002859 if ((int)dp->db_free >= space_needed) // enough room in data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002860 {
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002861 /*
2862 * Insert the new line in an existing data block, or in the data block
2863 * allocated above.
2864 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002865 dp->db_txt_start -= len;
2866 dp->db_free -= space_needed;
2867 ++(dp->db_line_count);
2868
2869 /*
2870 * move the text of the lines that follow to the front
2871 * adjust the indexes of the lines that follow
2872 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002873 if (line_count > db_idx + 1) // if there are following lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00002874 {
2875 /*
2876 * Offset is the start of the previous line.
2877 * This will become the character just after the new line.
2878 */
2879 if (db_idx < 0)
2880 offset = dp->db_txt_end;
2881 else
2882 offset = ((dp->db_index[db_idx]) & DB_INDEX_MASK);
2883 mch_memmove((char *)dp + dp->db_txt_start,
2884 (char *)dp + dp->db_txt_start + len,
2885 (size_t)(offset - (dp->db_txt_start + len)));
2886 for (i = line_count - 1; i > db_idx; --i)
2887 dp->db_index[i + 1] = dp->db_index[i] - len;
2888 dp->db_index[db_idx + 1] = offset - len;
2889 }
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002890 else
2891 // add line at the end (which is the start of the text)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002892 dp->db_index[db_idx + 1] = dp->db_txt_start;
2893
2894 /*
2895 * copy the text into the block
2896 */
2897 mch_memmove((char *)dp + dp->db_index[db_idx + 1], line, (size_t)len);
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002898 if (flags & ML_APPEND_MARK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002899 dp->db_index[db_idx + 1] |= DB_MARKED;
2900
2901 /*
2902 * Mark the block dirty.
2903 */
2904 buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002905 if (!(flags & ML_APPEND_NEW))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002906 buf->b_ml.ml_flags |= ML_LOCKED_POS;
2907 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002908 else // not enough space in data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002909 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002910 long line_count_left, line_count_right;
2911 int page_count_left, page_count_right;
2912 bhdr_T *hp_left;
2913 bhdr_T *hp_right;
2914 bhdr_T *hp_new;
2915 int lines_moved;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002916 int data_moved = 0; // init to shut up gcc
2917 int total_moved = 0; // init to shut up gcc
Bram Moolenaar071d4272004-06-13 20:20:40 +00002918 DATA_BL *dp_right, *dp_left;
2919 int stack_idx;
2920 int in_left;
2921 int lineadd;
2922 blocknr_T bnum_left, bnum_right;
2923 linenr_T lnum_left, lnum_right;
2924 int pb_idx;
2925 PTR_BL *pp_new;
2926
2927 /*
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002928 * There is not enough room, we have to create a new data block and
2929 * copy some lines into it.
2930 * Then we have to insert an entry in the pointer block.
2931 * If this pointer block also is full, we go up another block, and so
2932 * on, up to the root if necessary.
2933 * The line counts in the pointer blocks have already been adjusted by
2934 * ml_find_line().
2935 *
Bram Moolenaar071d4272004-06-13 20:20:40 +00002936 * We are going to allocate a new data block. Depending on the
2937 * situation it will be put to the left or right of the existing
2938 * block. If possible we put the new line in the left block and move
2939 * the lines after it to the right block. Otherwise the new line is
2940 * also put in the right block. This method is more efficient when
2941 * inserting a lot of lines at one place.
2942 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002943 if (db_idx < 0) // left block is new, right block is existing
Bram Moolenaar071d4272004-06-13 20:20:40 +00002944 {
2945 lines_moved = 0;
2946 in_left = TRUE;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002947 // space_needed does not change
Bram Moolenaar071d4272004-06-13 20:20:40 +00002948 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002949 else // left block is existing, right block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00002950 {
2951 lines_moved = line_count - db_idx - 1;
2952 if (lines_moved == 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002953 in_left = FALSE; // put new line in right block
2954 // space_needed does not change
Bram Moolenaar071d4272004-06-13 20:20:40 +00002955 else
2956 {
2957 data_moved = ((dp->db_index[db_idx]) & DB_INDEX_MASK) -
2958 dp->db_txt_start;
2959 total_moved = data_moved + lines_moved * INDEX_SIZE;
2960 if ((int)dp->db_free + total_moved >= space_needed)
2961 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002962 in_left = TRUE; // put new line in left block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002963 space_needed = total_moved;
2964 }
2965 else
2966 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002967 in_left = FALSE; // put new line in right block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002968 space_needed += total_moved;
2969 }
2970 }
2971 }
2972
2973 page_count = ((space_needed + HEADER_SIZE) + page_size - 1) / page_size;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002974 if ((hp_new = ml_new_data(mfp, flags & ML_APPEND_NEW, page_count))
2975 == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002976 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002977 // correct line counts in pointer blocks
Bram Moolenaar071d4272004-06-13 20:20:40 +00002978 --(buf->b_ml.ml_locked_lineadd);
2979 --(buf->b_ml.ml_locked_high);
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002980 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002981 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002982 if (db_idx < 0) // left block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00002983 {
2984 hp_left = hp_new;
2985 hp_right = hp;
2986 line_count_left = 0;
2987 line_count_right = line_count;
2988 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002989 else // right block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00002990 {
2991 hp_left = hp;
2992 hp_right = hp_new;
2993 line_count_left = line_count;
2994 line_count_right = 0;
2995 }
2996 dp_right = (DATA_BL *)(hp_right->bh_data);
2997 dp_left = (DATA_BL *)(hp_left->bh_data);
2998 bnum_left = hp_left->bh_bnum;
2999 bnum_right = hp_right->bh_bnum;
3000 page_count_left = hp_left->bh_page_count;
3001 page_count_right = hp_right->bh_page_count;
3002
3003 /*
3004 * May move the new line into the right/new block.
3005 */
3006 if (!in_left)
3007 {
3008 dp_right->db_txt_start -= len;
3009 dp_right->db_free -= len + INDEX_SIZE;
3010 dp_right->db_index[0] = dp_right->db_txt_start;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003011 if (flags & ML_APPEND_MARK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003012 dp_right->db_index[0] |= DB_MARKED;
3013
3014 mch_memmove((char *)dp_right + dp_right->db_txt_start,
3015 line, (size_t)len);
3016 ++line_count_right;
3017 }
3018 /*
3019 * may move lines from the left/old block to the right/new one.
3020 */
3021 if (lines_moved)
3022 {
3023 /*
3024 */
3025 dp_right->db_txt_start -= data_moved;
3026 dp_right->db_free -= total_moved;
3027 mch_memmove((char *)dp_right + dp_right->db_txt_start,
3028 (char *)dp_left + dp_left->db_txt_start,
3029 (size_t)data_moved);
3030 offset = dp_right->db_txt_start - dp_left->db_txt_start;
3031 dp_left->db_txt_start += data_moved;
3032 dp_left->db_free += total_moved;
3033
3034 /*
3035 * update indexes in the new block
3036 */
3037 for (to = line_count_right, from = db_idx + 1;
3038 from < line_count_left; ++from, ++to)
3039 dp_right->db_index[to] = dp->db_index[from] + offset;
3040 line_count_right += lines_moved;
3041 line_count_left -= lines_moved;
3042 }
3043
3044 /*
3045 * May move the new line into the left (old or new) block.
3046 */
3047 if (in_left)
3048 {
3049 dp_left->db_txt_start -= len;
3050 dp_left->db_free -= len + INDEX_SIZE;
3051 dp_left->db_index[line_count_left] = dp_left->db_txt_start;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003052 if (flags & ML_APPEND_MARK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003053 dp_left->db_index[line_count_left] |= DB_MARKED;
3054 mch_memmove((char *)dp_left + dp_left->db_txt_start,
3055 line, (size_t)len);
3056 ++line_count_left;
3057 }
3058
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003059 if (db_idx < 0) // left block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00003060 {
3061 lnum_left = lnum + 1;
3062 lnum_right = 0;
3063 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003064 else // right block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00003065 {
3066 lnum_left = 0;
3067 if (in_left)
3068 lnum_right = lnum + 2;
3069 else
3070 lnum_right = lnum + 1;
3071 }
3072 dp_left->db_line_count = line_count_left;
3073 dp_right->db_line_count = line_count_right;
3074
3075 /*
3076 * release the two data blocks
3077 * The new one (hp_new) already has a correct blocknumber.
3078 * The old one (hp, in ml_locked) gets a positive blocknumber if
3079 * we changed it and we are not editing a new file.
3080 */
3081 if (lines_moved || in_left)
3082 buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003083 if (!(flags & ML_APPEND_NEW) && db_idx >= 0 && in_left)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003084 buf->b_ml.ml_flags |= ML_LOCKED_POS;
3085 mf_put(mfp, hp_new, TRUE, FALSE);
3086
3087 /*
3088 * flush the old data block
3089 * set ml_locked_lineadd to 0, because the updating of the
3090 * pointer blocks is done below
3091 */
3092 lineadd = buf->b_ml.ml_locked_lineadd;
3093 buf->b_ml.ml_locked_lineadd = 0;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003094 ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003095
3096 /*
3097 * update pointer blocks for the new data block
3098 */
3099 for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0;
3100 --stack_idx)
3101 {
3102 ip = &(buf->b_ml.ml_stack[stack_idx]);
3103 pb_idx = ip->ip_index;
3104 if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003105 goto theend;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003106 pp = (PTR_BL *)(hp->bh_data); // must be pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003107 if (pp->pb_id != PTR_ID)
3108 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003109 iemsg(_("E317: pointer block id wrong 3"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003110 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003111 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003112 }
3113 /*
3114 * TODO: If the pointer block is full and we are adding at the end
3115 * try to insert in front of the next block
3116 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003117 // block not full, add one entry
Bram Moolenaar071d4272004-06-13 20:20:40 +00003118 if (pp->pb_count < pp->pb_count_max)
3119 {
3120 if (pb_idx + 1 < (int)pp->pb_count)
3121 mch_memmove(&pp->pb_pointer[pb_idx + 2],
3122 &pp->pb_pointer[pb_idx + 1],
3123 (size_t)(pp->pb_count - pb_idx - 1) * sizeof(PTR_EN));
3124 ++pp->pb_count;
3125 pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
3126 pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
3127 pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
3128 pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right;
3129 pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right;
3130 pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right;
3131
3132 if (lnum_left != 0)
3133 pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left;
3134 if (lnum_right != 0)
3135 pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right;
3136
3137 mf_put(mfp, hp, TRUE, FALSE);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003138 buf->b_ml.ml_stack_top = stack_idx + 1; // truncate stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00003139
3140 if (lineadd)
3141 {
3142 --(buf->b_ml.ml_stack_top);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003143 // fix line count for rest of blocks in the stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00003144 ml_lineadd(buf, lineadd);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003145 // fix stack itself
Bram Moolenaar071d4272004-06-13 20:20:40 +00003146 buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high +=
3147 lineadd;
3148 ++(buf->b_ml.ml_stack_top);
3149 }
3150
3151 /*
3152 * We are finished, break the loop here.
3153 */
3154 break;
3155 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003156 else // pointer block full
Bram Moolenaar071d4272004-06-13 20:20:40 +00003157 {
3158 /*
3159 * split the pointer block
3160 * allocate a new pointer block
3161 * move some of the pointer into the new block
3162 * prepare for updating the parent block
3163 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003164 for (;;) // do this twice when splitting block 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00003165 {
3166 hp_new = ml_new_ptr(mfp);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003167 if (hp_new == NULL) // TODO: try to fix tree
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003168 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003169 pp_new = (PTR_BL *)(hp_new->bh_data);
3170
3171 if (hp->bh_bnum != 1)
3172 break;
3173
3174 /*
3175 * if block 1 becomes full the tree is given an extra level
3176 * The pointers from block 1 are moved into the new block.
3177 * block 1 is updated to point to the new block
3178 * then continue to split the new block
3179 */
3180 mch_memmove(pp_new, pp, (size_t)page_size);
3181 pp->pb_count = 1;
3182 pp->pb_pointer[0].pe_bnum = hp_new->bh_bnum;
3183 pp->pb_pointer[0].pe_line_count = buf->b_ml.ml_line_count;
3184 pp->pb_pointer[0].pe_old_lnum = 1;
3185 pp->pb_pointer[0].pe_page_count = 1;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003186 mf_put(mfp, hp, TRUE, FALSE); // release block 1
3187 hp = hp_new; // new block is to be split
Bram Moolenaar071d4272004-06-13 20:20:40 +00003188 pp = pp_new;
3189 CHECK(stack_idx != 0, _("stack_idx should be 0"));
3190 ip->ip_index = 0;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003191 ++stack_idx; // do block 1 again later
Bram Moolenaar071d4272004-06-13 20:20:40 +00003192 }
3193 /*
3194 * move the pointers after the current one to the new block
3195 * If there are none, the new entry will be in the new block.
3196 */
3197 total_moved = pp->pb_count - pb_idx - 1;
3198 if (total_moved)
3199 {
3200 mch_memmove(&pp_new->pb_pointer[0],
3201 &pp->pb_pointer[pb_idx + 1],
3202 (size_t)(total_moved) * sizeof(PTR_EN));
3203 pp_new->pb_count = total_moved;
3204 pp->pb_count -= total_moved - 1;
3205 pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right;
3206 pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right;
3207 pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right;
3208 if (lnum_right)
3209 pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right;
3210 }
3211 else
3212 {
3213 pp_new->pb_count = 1;
3214 pp_new->pb_pointer[0].pe_bnum = bnum_right;
3215 pp_new->pb_pointer[0].pe_line_count = line_count_right;
3216 pp_new->pb_pointer[0].pe_page_count = page_count_right;
3217 pp_new->pb_pointer[0].pe_old_lnum = lnum_right;
3218 }
3219 pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
3220 pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
3221 pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
3222 if (lnum_left)
3223 pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left;
3224 lnum_left = 0;
3225 lnum_right = 0;
3226
3227 /*
3228 * recompute line counts
3229 */
3230 line_count_right = 0;
3231 for (i = 0; i < (int)pp_new->pb_count; ++i)
3232 line_count_right += pp_new->pb_pointer[i].pe_line_count;
3233 line_count_left = 0;
3234 for (i = 0; i < (int)pp->pb_count; ++i)
3235 line_count_left += pp->pb_pointer[i].pe_line_count;
3236
3237 bnum_left = hp->bh_bnum;
3238 bnum_right = hp_new->bh_bnum;
3239 page_count_left = 1;
3240 page_count_right = 1;
3241 mf_put(mfp, hp, TRUE, FALSE);
3242 mf_put(mfp, hp_new, TRUE, FALSE);
3243 }
3244 }
3245
3246 /*
3247 * Safety check: fallen out of for loop?
3248 */
3249 if (stack_idx < 0)
3250 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003251 iemsg(_("E318: Updated too many blocks?"));
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003252 buf->b_ml.ml_stack_top = 0; // invalidate stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00003253 }
3254 }
3255
3256#ifdef FEAT_BYTEOFF
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003257 // The line was inserted below 'lnum'
Bram Moolenaar071d4272004-06-13 20:20:40 +00003258 ml_updatechunk(buf, lnum + 1, (long)len, ML_CHNK_ADDLINE);
3259#endif
3260#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02003261 if (netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +00003262 {
3263 if (STRLEN(line) > 0)
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003264 netbeans_inserted(buf, lnum+1, (colnr_T)0, line, (int)STRLEN(line));
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00003265 netbeans_inserted(buf, lnum+1, (colnr_T)STRLEN(line),
Bram Moolenaar071d4272004-06-13 20:20:40 +00003266 (char_u *)"\n", 1);
3267 }
3268#endif
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01003269#ifdef FEAT_JOB_CHANNEL
Bram Moolenaar99ef0622016-03-06 20:22:25 +01003270 if (buf->b_write_to_channel)
3271 channel_write_new_lines(buf);
3272#endif
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003273 ret = OK;
Bram Moolenaar99ef0622016-03-06 20:22:25 +01003274
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003275theend:
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003276#ifdef FEAT_PROP_POPUP
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003277 vim_free(tofree);
3278#endif
3279 return ret;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003280}
3281
3282/*
Bram Moolenaar250e3112019-07-15 23:02:14 +02003283 * Flush any pending change and call ml_append_int()
3284 */
3285 static int
3286ml_append_flush(
3287 buf_T *buf,
3288 linenr_T lnum, // append after this line (can be 0)
3289 char_u *line, // text of the new line
3290 colnr_T len, // length of line, including NUL, or 0
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003291 int flags) // ML_APPEND_ flags
Bram Moolenaar250e3112019-07-15 23:02:14 +02003292{
3293 if (lnum > buf->b_ml.ml_line_count)
3294 return FAIL; // lnum out of range
3295
3296 if (buf->b_ml.ml_line_lnum != 0)
3297 // This may also invoke ml_append_int().
3298 ml_flush_line(buf);
3299
3300#ifdef FEAT_EVAL
3301 // When inserting above recorded changes: flush the changes before changing
3302 // the text. Then flush the cached line, it may become invalid.
3303 may_invoke_listeners(buf, lnum + 1, lnum + 1, 1);
3304 if (buf->b_ml.ml_line_lnum != 0)
3305 ml_flush_line(buf);
3306#endif
3307
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003308 return ml_append_int(buf, lnum, line, len, flags);
Bram Moolenaar250e3112019-07-15 23:02:14 +02003309}
3310
3311/*
3312 * Append a line after lnum (may be 0 to insert a line in front of the file).
3313 * "line" does not need to be allocated, but can't be another line in a
3314 * buffer, unlocking may make it invalid.
3315 *
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003316 * "newfile": TRUE when starting to edit a new file, meaning that pe_old_lnum
Bram Moolenaar250e3112019-07-15 23:02:14 +02003317 * will be set for recovery
3318 * Check: The caller of this function should probably also call
3319 * appended_lines().
3320 *
3321 * return FAIL for failure, OK otherwise
3322 */
3323 int
3324ml_append(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003325 linenr_T lnum, // append after this line (can be 0)
3326 char_u *line, // text of the new line
3327 colnr_T len, // length of new line, including NUL, or 0
3328 int newfile) // flag, see above
Bram Moolenaar250e3112019-07-15 23:02:14 +02003329{
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003330 return ml_append_flags(lnum, line, len, newfile ? ML_APPEND_NEW : 0);
3331}
3332
3333 int
3334ml_append_flags(
3335 linenr_T lnum, // append after this line (can be 0)
3336 char_u *line, // text of the new line
3337 colnr_T len, // length of new line, including NUL, or 0
3338 int flags) // ML_APPEND_ values
3339{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003340 // When starting up, we might still need to create the memfile
Bram Moolenaar250e3112019-07-15 23:02:14 +02003341 if (curbuf->b_ml.ml_mfp == NULL && open_buffer(FALSE, NULL, 0) == FAIL)
3342 return FAIL;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003343 return ml_append_flush(curbuf, lnum, line, len, flags);
Bram Moolenaar250e3112019-07-15 23:02:14 +02003344}
3345
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003346
Bram Moolenaar250e3112019-07-15 23:02:14 +02003347#if defined(FEAT_SPELL) || defined(FEAT_QUICKFIX) || defined(PROTO)
3348/*
3349 * Like ml_append() but for an arbitrary buffer. The buffer must already have
3350 * a memline.
3351 */
3352 int
3353ml_append_buf(
3354 buf_T *buf,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003355 linenr_T lnum, // append after this line (can be 0)
3356 char_u *line, // text of the new line
3357 colnr_T len, // length of new line, including NUL, or 0
3358 int newfile) // flag, see above
Bram Moolenaar250e3112019-07-15 23:02:14 +02003359{
3360 if (buf->b_ml.ml_mfp == NULL)
3361 return FAIL;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003362 return ml_append_flush(buf, lnum, line, len, newfile ? ML_APPEND_NEW : 0);
Bram Moolenaar250e3112019-07-15 23:02:14 +02003363}
3364#endif
3365
3366/*
Bram Moolenaar5966ea12020-07-15 15:30:05 +02003367 * Replace line "lnum", with buffering, in current buffer.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003368 *
Bram Moolenaar1056d982006-03-09 22:37:52 +00003369 * If "copy" is TRUE, make a copy of the line, otherwise the line has been
Bram Moolenaar071d4272004-06-13 20:20:40 +00003370 * copied to allocated memory already.
Bram Moolenaar5966ea12020-07-15 15:30:05 +02003371 * If "copy" is FALSE the "line" may be freed to add text properties!
3372 * Do not use it after calling ml_replace().
Bram Moolenaar071d4272004-06-13 20:20:40 +00003373 *
3374 * Check: The caller of this function should probably also call
3375 * changed_lines(), unless update_screen(NOT_VALID) is used.
3376 *
3377 * return FAIL for failure, OK otherwise
3378 */
3379 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003380ml_replace(linenr_T lnum, char_u *line, int copy)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003381{
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003382 colnr_T len = -1;
3383
3384 if (line != NULL)
Bram Moolenaar4efe73b2018-12-16 14:37:39 +01003385 len = (colnr_T)STRLEN(line);
Bram Moolenaarccae4672019-01-04 15:09:57 +01003386 return ml_replace_len(lnum, line, len, FALSE, copy);
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003387}
3388
Bram Moolenaarccae4672019-01-04 15:09:57 +01003389/*
3390 * Replace a line for the current buffer. Like ml_replace() with:
3391 * "len_arg" is the length of the text, excluding NUL.
3392 * If "has_props" is TRUE then "line_arg" includes the text properties and
3393 * "len_arg" includes the NUL of the text.
3394 */
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003395 int
Bram Moolenaarccae4672019-01-04 15:09:57 +01003396ml_replace_len(
3397 linenr_T lnum,
3398 char_u *line_arg,
3399 colnr_T len_arg,
3400 int has_props,
3401 int copy)
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003402{
3403 char_u *line = line_arg;
3404 colnr_T len = len_arg;
3405
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003406 if (line == NULL) // just checking...
Bram Moolenaar071d4272004-06-13 20:20:40 +00003407 return FAIL;
3408
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003409 // When starting up, we might still need to create the memfile
Bram Moolenaar59f931e2010-07-24 20:27:03 +02003410 if (curbuf->b_ml.ml_mfp == NULL && open_buffer(FALSE, NULL, 0) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003411 return FAIL;
3412
Bram Moolenaarccae4672019-01-04 15:09:57 +01003413 if (!has_props)
3414 ++len; // include the NUL after the text
3415 if (copy)
3416 {
3417 // copy the line to allocated memory
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003418#ifdef FEAT_PROP_POPUP
Bram Moolenaarccae4672019-01-04 15:09:57 +01003419 if (has_props)
3420 line = vim_memsave(line, len);
3421 else
3422#endif
3423 line = vim_strnsave(line, len - 1);
3424 if (line == NULL)
3425 return FAIL;
3426 }
3427
Bram Moolenaar071d4272004-06-13 20:20:40 +00003428#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02003429 if (netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +00003430 {
3431 netbeans_removed(curbuf, lnum, 0, (long)STRLEN(ml_get(lnum)));
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003432 netbeans_inserted(curbuf, lnum, 0, line, (int)STRLEN(line));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003433 }
3434#endif
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003435 if (curbuf->b_ml.ml_line_lnum != lnum)
3436 {
3437 // another line is buffered, flush it
3438 ml_flush_line(curbuf);
Bram Moolenaarca79a5f2018-12-13 23:16:36 +01003439 curbuf->b_ml.ml_flags &= ~ML_LINE_DIRTY;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003440
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003441#ifdef FEAT_PROP_POPUP
Bram Moolenaarccae4672019-01-04 15:09:57 +01003442 if (curbuf->b_has_textprop && !has_props)
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003443 // Need to fetch the old line to copy over any text properties.
3444 ml_get_buf(curbuf, lnum, TRUE);
3445#endif
3446 }
3447
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003448#ifdef FEAT_PROP_POPUP
Bram Moolenaarccae4672019-01-04 15:09:57 +01003449 if (curbuf->b_has_textprop && !has_props)
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003450 {
3451 size_t oldtextlen = STRLEN(curbuf->b_ml.ml_line_ptr) + 1;
3452
3453 if (oldtextlen < (size_t)curbuf->b_ml.ml_line_len)
3454 {
3455 char_u *newline;
3456 size_t textproplen = curbuf->b_ml.ml_line_len - oldtextlen;
3457
3458 // Need to copy over text properties, stored after the text.
Bram Moolenaarccae4672019-01-04 15:09:57 +01003459 newline = alloc(len + (int)textproplen);
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003460 if (newline != NULL)
3461 {
Bram Moolenaarccae4672019-01-04 15:09:57 +01003462 mch_memmove(newline, line, len);
Bram Moolenaar0d3cb732019-05-18 13:05:18 +02003463 mch_memmove(newline + len, curbuf->b_ml.ml_line_ptr
3464 + oldtextlen, textproplen);
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003465 vim_free(line);
3466 line = newline;
Bram Moolenaar4efe73b2018-12-16 14:37:39 +01003467 len += (colnr_T)textproplen;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003468 }
3469 }
3470 }
3471#endif
3472
Bram Moolenaarccae4672019-01-04 15:09:57 +01003473 if (curbuf->b_ml.ml_flags & ML_LINE_DIRTY) // same line allocated
3474 vim_free(curbuf->b_ml.ml_line_ptr); // free it
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003475
Bram Moolenaar071d4272004-06-13 20:20:40 +00003476 curbuf->b_ml.ml_line_ptr = line;
Bram Moolenaarccae4672019-01-04 15:09:57 +01003477 curbuf->b_ml.ml_line_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003478 curbuf->b_ml.ml_line_lnum = lnum;
3479 curbuf->b_ml.ml_flags = (curbuf->b_ml.ml_flags | ML_LINE_DIRTY) & ~ML_EMPTY;
3480
3481 return OK;
3482}
3483
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003484#ifdef FEAT_PROP_POPUP
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003485/*
3486 * Adjust text properties in line "lnum" for a deleted line.
3487 * When "above" is true this is the line above the deleted line.
3488 * "del_props" are the properties of the deleted line.
3489 */
3490 static void
3491adjust_text_props_for_delete(
3492 buf_T *buf,
3493 linenr_T lnum,
3494 char_u *del_props,
3495 int del_props_len,
3496 int above)
3497{
3498 int did_get_line = FALSE;
3499 int done_del;
3500 int done_this;
3501 textprop_T prop_del;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003502 bhdr_T *hp;
3503 DATA_BL *dp;
3504 int idx;
3505 int line_start;
3506 long line_size;
3507 int this_props_len;
3508 char_u *text;
3509 size_t textlen;
3510 int found;
3511
3512 for (done_del = 0; done_del < del_props_len; done_del += sizeof(textprop_T))
3513 {
3514 mch_memmove(&prop_del, del_props + done_del, sizeof(textprop_T));
3515 if ((above && (prop_del.tp_flags & TP_FLAG_CONT_PREV)
3516 && !(prop_del.tp_flags & TP_FLAG_CONT_NEXT))
3517 || (!above && (prop_del.tp_flags & TP_FLAG_CONT_NEXT)
3518 && !(prop_del.tp_flags & TP_FLAG_CONT_PREV)))
3519 {
3520 if (!did_get_line)
3521 {
3522 did_get_line = TRUE;
3523 if ((hp = ml_find_line(buf, lnum, ML_FIND)) == NULL)
3524 return;
3525
3526 dp = (DATA_BL *)(hp->bh_data);
3527 idx = lnum - buf->b_ml.ml_locked_low;
3528 line_start = ((dp->db_index[idx]) & DB_INDEX_MASK);
3529 if (idx == 0) // first line in block, text at the end
3530 line_size = dp->db_txt_end - line_start;
3531 else
Bram Moolenaar87be9be2020-05-30 15:32:02 +02003532 line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK)
3533 - line_start;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003534 text = (char_u *)dp + line_start;
3535 textlen = STRLEN(text) + 1;
3536 if ((long)textlen >= line_size)
3537 {
3538 if (above)
3539 internal_error("no text property above deleted line");
3540 else
3541 internal_error("no text property below deleted line");
3542 return;
3543 }
Bram Moolenaar4b7214e2019-01-03 21:55:32 +01003544 this_props_len = line_size - (int)textlen;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003545 }
3546
3547 found = FALSE;
Bram Moolenaar87be9be2020-05-30 15:32:02 +02003548 for (done_this = 0; done_this < this_props_len;
3549 done_this += sizeof(textprop_T))
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003550 {
Bram Moolenaar87be9be2020-05-30 15:32:02 +02003551 int flag = above ? TP_FLAG_CONT_NEXT
3552 : TP_FLAG_CONT_PREV;
3553 textprop_T prop_this;
3554
3555 mch_memmove(&prop_this, text + textlen + done_del,
3556 sizeof(textprop_T));
3557 if ((prop_this.tp_flags & flag)
3558 && prop_del.tp_id == prop_this.tp_id
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003559 && prop_del.tp_type == prop_this.tp_type)
3560 {
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003561 found = TRUE;
Bram Moolenaar87be9be2020-05-30 15:32:02 +02003562 prop_this.tp_flags &= ~flag;
3563 mch_memmove(text + textlen + done_del, &prop_this,
3564 sizeof(textprop_T));
3565 break;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003566 }
3567 }
3568 if (!found)
3569 {
3570 if (above)
3571 internal_error("text property above deleted line not found");
3572 else
3573 internal_error("text property below deleted line not found");
3574 }
3575
3576 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
3577 }
3578 }
3579}
3580#endif
3581
Bram Moolenaar071d4272004-06-13 20:20:40 +00003582/*
Bram Moolenaar4033c552017-09-16 20:54:51 +02003583 * Delete line "lnum" in the current buffer.
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003584 * When "flags" has ML_DEL_MESSAGE may give a "No lines in buffer" message.
3585 * When "flags" has ML_DEL_UNDO this is called from undo.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003586 *
3587 * return FAIL for failure, OK otherwise
3588 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003589 static int
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003590ml_delete_int(buf_T *buf, linenr_T lnum, int flags)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003591{
3592 bhdr_T *hp;
3593 memfile_T *mfp;
3594 DATA_BL *dp;
3595 PTR_BL *pp;
3596 infoptr_T *ip;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003597 int count; // number of entries in block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003598 int idx;
3599 int stack_idx;
3600 int text_start;
3601 int line_start;
3602 long line_size;
3603 int i;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003604 int ret = FAIL;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003605#ifdef FEAT_PROP_POPUP
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003606 char_u *textprop_save = NULL;
3607 int textprop_save_len;
3608#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003609
Bram Moolenaar071d4272004-06-13 20:20:40 +00003610 if (lowest_marked && lowest_marked > lnum)
3611 lowest_marked--;
3612
3613/*
3614 * If the file becomes empty the last line is replaced by an empty line.
3615 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003616 if (buf->b_ml.ml_line_count == 1) // file becomes empty
Bram Moolenaar071d4272004-06-13 20:20:40 +00003617 {
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003618 if ((flags & ML_DEL_MESSAGE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003619#ifdef FEAT_NETBEANS_INTG
3620 && !netbeansSuppressNoLines
3621#endif
3622 )
Bram Moolenaar238a5642006-02-21 22:12:05 +00003623 set_keep_msg((char_u *)_(no_lines_msg), 0);
3624
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003625 // FEAT_BYTEOFF already handled in there, don't worry 'bout it below
Bram Moolenaar071d4272004-06-13 20:20:40 +00003626 i = ml_replace((linenr_T)1, (char_u *)"", TRUE);
3627 buf->b_ml.ml_flags |= ML_EMPTY;
3628
3629 return i;
3630 }
3631
3632/*
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003633 * Find the data block containing the line.
3634 * This also fills the stack with the blocks from the root to the data block.
3635 * This also releases any locked block..
Bram Moolenaar071d4272004-06-13 20:20:40 +00003636 */
3637 mfp = buf->b_ml.ml_mfp;
3638 if (mfp == NULL)
3639 return FAIL;
3640
3641 if ((hp = ml_find_line(buf, lnum, ML_DELETE)) == NULL)
3642 return FAIL;
3643
3644 dp = (DATA_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003645 // compute line count before the delete
Bram Moolenaar071d4272004-06-13 20:20:40 +00003646 count = (long)(buf->b_ml.ml_locked_high)
3647 - (long)(buf->b_ml.ml_locked_low) + 2;
3648 idx = lnum - buf->b_ml.ml_locked_low;
3649
3650 --buf->b_ml.ml_line_count;
3651
3652 line_start = ((dp->db_index[idx]) & DB_INDEX_MASK);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003653 if (idx == 0) // first line in block, text at the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00003654 line_size = dp->db_txt_end - line_start;
3655 else
3656 line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK) - line_start;
3657
3658#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02003659 if (netbeans_active())
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00003660 netbeans_removed(buf, lnum, 0, (long)line_size);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003661#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003662#ifdef FEAT_PROP_POPUP
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003663 // If there are text properties, make a copy, so that we can update
3664 // properties in preceding and following lines.
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003665 if (buf->b_has_textprop && !(flags & ML_DEL_UNDO))
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003666 {
3667 size_t textlen = STRLEN((char_u *)dp + line_start) + 1;
3668
3669 if ((long)textlen < line_size)
3670 {
Bram Moolenaar4b7214e2019-01-03 21:55:32 +01003671 textprop_save_len = line_size - (int)textlen;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003672 textprop_save = vim_memsave((char_u *)dp + line_start + textlen,
3673 textprop_save_len);
3674 }
3675 }
3676#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003677
3678/*
3679 * special case: If there is only one line in the data block it becomes empty.
3680 * Then we have to remove the entry, pointing to this data block, from the
3681 * pointer block. If this pointer block also becomes empty, we go up another
3682 * block, and so on, up to the root if necessary.
3683 * The line counts in the pointer blocks have already been adjusted by
3684 * ml_find_line().
3685 */
3686 if (count == 1)
3687 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003688 mf_free(mfp, hp); // free the data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003689 buf->b_ml.ml_locked = NULL;
3690
Bram Moolenaare60acc12011-05-10 16:41:25 +02003691 for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0;
3692 --stack_idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003693 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003694 buf->b_ml.ml_stack_top = 0; // stack is invalid when failing
Bram Moolenaar071d4272004-06-13 20:20:40 +00003695 ip = &(buf->b_ml.ml_stack[stack_idx]);
3696 idx = ip->ip_index;
3697 if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003698 goto theend;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003699 pp = (PTR_BL *)(hp->bh_data); // must be pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003700 if (pp->pb_id != PTR_ID)
3701 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003702 iemsg(_("E317: pointer block id wrong 4"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003703 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003704 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003705 }
3706 count = --(pp->pb_count);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003707 if (count == 0) // the pointer block becomes empty!
Bram Moolenaar071d4272004-06-13 20:20:40 +00003708 mf_free(mfp, hp);
3709 else
3710 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003711 if (count != idx) // move entries after the deleted one
Bram Moolenaar071d4272004-06-13 20:20:40 +00003712 mch_memmove(&pp->pb_pointer[idx], &pp->pb_pointer[idx + 1],
3713 (size_t)(count - idx) * sizeof(PTR_EN));
3714 mf_put(mfp, hp, TRUE, FALSE);
3715
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003716 buf->b_ml.ml_stack_top = stack_idx; // truncate stack
3717 // fix line count for rest of blocks in the stack
Bram Moolenaar6b803a72007-05-06 14:25:46 +00003718 if (buf->b_ml.ml_locked_lineadd != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003719 {
3720 ml_lineadd(buf, buf->b_ml.ml_locked_lineadd);
3721 buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high +=
Bram Moolenaar6b803a72007-05-06 14:25:46 +00003722 buf->b_ml.ml_locked_lineadd;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003723 }
3724 ++(buf->b_ml.ml_stack_top);
3725
3726 break;
3727 }
3728 }
3729 CHECK(stack_idx < 0, _("deleted block 1?"));
3730 }
3731 else
3732 {
3733 /*
3734 * delete the text by moving the next lines forwards
3735 */
3736 text_start = dp->db_txt_start;
3737 mch_memmove((char *)dp + text_start + line_size,
3738 (char *)dp + text_start, (size_t)(line_start - text_start));
3739
3740 /*
3741 * delete the index by moving the next indexes backwards
3742 * Adjust the indexes for the text movement.
3743 */
3744 for (i = idx; i < count - 1; ++i)
3745 dp->db_index[i] = dp->db_index[i + 1] + line_size;
3746
3747 dp->db_free += line_size + INDEX_SIZE;
3748 dp->db_txt_start += line_size;
3749 --(dp->db_line_count);
3750
3751 /*
3752 * mark the block dirty and make sure it is in the file (for recovery)
3753 */
3754 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
3755 }
3756
3757#ifdef FEAT_BYTEOFF
3758 ml_updatechunk(buf, lnum, line_size, ML_CHNK_DELLINE);
3759#endif
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003760 ret = OK;
3761
3762theend:
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003763#ifdef FEAT_PROP_POPUP
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003764 if (textprop_save != NULL)
3765 {
3766 // Adjust text properties in the line above and below.
3767 if (lnum > 1)
3768 adjust_text_props_for_delete(buf, lnum - 1, textprop_save, textprop_save_len, TRUE);
3769 if (lnum <= buf->b_ml.ml_line_count)
3770 adjust_text_props_for_delete(buf, lnum, textprop_save, textprop_save_len, FALSE);
3771 }
3772 vim_free(textprop_save);
3773#endif
3774 return ret;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003775}
3776
3777/*
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003778 * Delete line "lnum" in the current buffer.
3779 * When "message" is TRUE may give a "No lines in buffer" message.
3780 *
3781 * Check: The caller of this function should probably also call
3782 * deleted_lines() after this.
3783 *
3784 * return FAIL for failure, OK otherwise
3785 */
3786 int
Bram Moolenaarca70c072020-05-30 20:30:46 +02003787ml_delete(linenr_T lnum)
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003788{
Bram Moolenaarca70c072020-05-30 20:30:46 +02003789 return ml_delete_flags(lnum, 0);
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003790}
3791
3792/*
3793 * Like ml_delete() but using flags (see ml_delete_int()).
3794 */
3795 int
3796ml_delete_flags(linenr_T lnum, int flags)
3797{
3798 ml_flush_line(curbuf);
3799 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count)
3800 return FAIL;
3801
3802#ifdef FEAT_EVAL
3803 // When inserting above recorded changes: flush the changes before changing
3804 // the text.
3805 may_invoke_listeners(curbuf, lnum, lnum + 1, -1);
3806#endif
3807
3808 return ml_delete_int(curbuf, lnum, flags);
3809}
3810
3811/*
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003812 * set the DB_MARKED flag for line 'lnum'
Bram Moolenaar071d4272004-06-13 20:20:40 +00003813 */
3814 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003815ml_setmarked(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003816{
3817 bhdr_T *hp;
3818 DATA_BL *dp;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003819 // invalid line number
Bram Moolenaar071d4272004-06-13 20:20:40 +00003820 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count
3821 || curbuf->b_ml.ml_mfp == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003822 return; // give error message?
Bram Moolenaar071d4272004-06-13 20:20:40 +00003823
3824 if (lowest_marked == 0 || lowest_marked > lnum)
3825 lowest_marked = lnum;
3826
3827 /*
3828 * find the data block containing the line
3829 * This also fills the stack with the blocks from the root to the data block
3830 * This also releases any locked block.
3831 */
3832 if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003833 return; // give error message?
Bram Moolenaar071d4272004-06-13 20:20:40 +00003834
3835 dp = (DATA_BL *)(hp->bh_data);
3836 dp->db_index[lnum - curbuf->b_ml.ml_locked_low] |= DB_MARKED;
3837 curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
3838}
3839
3840/*
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003841 * find the first line with its DB_MARKED flag set
Bram Moolenaar071d4272004-06-13 20:20:40 +00003842 */
3843 linenr_T
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003844ml_firstmarked(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003845{
3846 bhdr_T *hp;
3847 DATA_BL *dp;
3848 linenr_T lnum;
3849 int i;
3850
3851 if (curbuf->b_ml.ml_mfp == NULL)
3852 return (linenr_T) 0;
3853
3854 /*
3855 * The search starts with lowest_marked line. This is the last line where
3856 * a mark was found, adjusted by inserting/deleting lines.
3857 */
3858 for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count; )
3859 {
3860 /*
3861 * Find the data block containing the line.
3862 * This also fills the stack with the blocks from the root to the data
3863 * block This also releases any locked block.
3864 */
3865 if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003866 return (linenr_T)0; // give error message?
Bram Moolenaar071d4272004-06-13 20:20:40 +00003867
3868 dp = (DATA_BL *)(hp->bh_data);
3869
3870 for (i = lnum - curbuf->b_ml.ml_locked_low;
3871 lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum)
3872 if ((dp->db_index[i]) & DB_MARKED)
3873 {
3874 (dp->db_index[i]) &= DB_INDEX_MASK;
3875 curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
3876 lowest_marked = lnum + 1;
3877 return lnum;
3878 }
3879 }
3880
3881 return (linenr_T) 0;
3882}
3883
Bram Moolenaar071d4272004-06-13 20:20:40 +00003884/*
3885 * clear all DB_MARKED flags
3886 */
3887 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003888ml_clearmarked(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003889{
3890 bhdr_T *hp;
3891 DATA_BL *dp;
3892 linenr_T lnum;
3893 int i;
3894
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003895 if (curbuf->b_ml.ml_mfp == NULL) // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00003896 return;
3897
3898 /*
3899 * The search starts with line lowest_marked.
3900 */
3901 for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count; )
3902 {
3903 /*
3904 * Find the data block containing the line.
3905 * This also fills the stack with the blocks from the root to the data
3906 * block and releases any locked block.
3907 */
3908 if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003909 return; // give error message?
Bram Moolenaar071d4272004-06-13 20:20:40 +00003910
3911 dp = (DATA_BL *)(hp->bh_data);
3912
3913 for (i = lnum - curbuf->b_ml.ml_locked_low;
3914 lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum)
3915 if ((dp->db_index[i]) & DB_MARKED)
3916 {
3917 (dp->db_index[i]) &= DB_INDEX_MASK;
3918 curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
3919 }
3920 }
3921
3922 lowest_marked = 0;
3923 return;
3924}
3925
3926/*
3927 * flush ml_line if necessary
3928 */
3929 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003930ml_flush_line(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003931{
3932 bhdr_T *hp;
3933 DATA_BL *dp;
3934 linenr_T lnum;
3935 char_u *new_line;
3936 char_u *old_line;
3937 colnr_T new_len;
3938 int old_len;
3939 int extra;
3940 int idx;
3941 int start;
3942 int count;
3943 int i;
Bram Moolenaar0ca4b352010-02-11 18:54:43 +01003944 static int entered = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003945
3946 if (buf->b_ml.ml_line_lnum == 0 || buf->b_ml.ml_mfp == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003947 return; // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00003948
3949 if (buf->b_ml.ml_flags & ML_LINE_DIRTY)
3950 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003951 // This code doesn't work recursively, but Netbeans may call back here
3952 // when obtaining the cursor position.
Bram Moolenaar0ca4b352010-02-11 18:54:43 +01003953 if (entered)
3954 return;
3955 entered = TRUE;
3956
Bram Moolenaar071d4272004-06-13 20:20:40 +00003957 lnum = buf->b_ml.ml_line_lnum;
3958 new_line = buf->b_ml.ml_line_ptr;
3959
3960 hp = ml_find_line(buf, lnum, ML_FIND);
3961 if (hp == NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003962 siemsg(_("E320: Cannot find line %ld"), lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003963 else
3964 {
3965 dp = (DATA_BL *)(hp->bh_data);
3966 idx = lnum - buf->b_ml.ml_locked_low;
3967 start = ((dp->db_index[idx]) & DB_INDEX_MASK);
3968 old_line = (char_u *)dp + start;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003969 if (idx == 0) // line is last in block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003970 old_len = dp->db_txt_end - start;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003971 else // text of previous line follows
Bram Moolenaar071d4272004-06-13 20:20:40 +00003972 old_len = (dp->db_index[idx - 1] & DB_INDEX_MASK) - start;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003973 new_len = buf->b_ml.ml_line_len;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003974 extra = new_len - old_len; // negative if lines gets smaller
Bram Moolenaar071d4272004-06-13 20:20:40 +00003975
3976 /*
3977 * if new line fits in data block, replace directly
3978 */
3979 if ((int)dp->db_free >= extra)
3980 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003981 // if the length changes and there are following lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00003982 count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 1;
3983 if (extra != 0 && idx < count - 1)
3984 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003985 // move text of following lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00003986 mch_memmove((char *)dp + dp->db_txt_start - extra,
3987 (char *)dp + dp->db_txt_start,
3988 (size_t)(start - dp->db_txt_start));
3989
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003990 // adjust pointers of this and following lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00003991 for (i = idx + 1; i < count; ++i)
3992 dp->db_index[i] -= extra;
3993 }
3994 dp->db_index[idx] -= extra;
3995
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003996 // adjust free space
Bram Moolenaar071d4272004-06-13 20:20:40 +00003997 dp->db_free -= extra;
3998 dp->db_txt_start -= extra;
3999
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004000 // copy new line into the data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00004001 mch_memmove(old_line - extra, new_line, (size_t)new_len);
4002 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
4003#ifdef FEAT_BYTEOFF
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004004 // The else case is already covered by the insert and delete
Bram Moolenaar071d4272004-06-13 20:20:40 +00004005 ml_updatechunk(buf, lnum, (long)extra, ML_CHNK_UPDLINE);
4006#endif
4007 }
4008 else
4009 {
4010 /*
4011 * Cannot do it in one data block: Delete and append.
4012 * Append first, because ml_delete_int() cannot delete the
4013 * last line in a buffer, which causes trouble for a buffer
4014 * that has only one line.
4015 * Don't forget to copy the mark!
4016 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004017 // How about handling errors???
Bram Moolenaara9d4b842020-05-30 14:46:52 +02004018 (void)ml_append_int(buf, lnum, new_line, new_len,
Bram Moolenaar840f91f2021-05-26 22:32:10 +02004019 ((dp->db_index[idx] & DB_MARKED) ? ML_APPEND_MARK : 0)
4020#ifdef FEAT_PROP_POPUP
4021 | ML_APPEND_NOPROP
4022#endif
4023 );
Bram Moolenaara9d4b842020-05-30 14:46:52 +02004024 (void)ml_delete_int(buf, lnum, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004025 }
4026 }
4027 vim_free(new_line);
Bram Moolenaar0ca4b352010-02-11 18:54:43 +01004028
4029 entered = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004030 }
4031
4032 buf->b_ml.ml_line_lnum = 0;
4033}
4034
4035/*
4036 * create a new, empty, data block
4037 */
4038 static bhdr_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004039ml_new_data(memfile_T *mfp, int negative, int page_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004040{
4041 bhdr_T *hp;
4042 DATA_BL *dp;
4043
4044 if ((hp = mf_new(mfp, negative, page_count)) == NULL)
4045 return NULL;
4046
4047 dp = (DATA_BL *)(hp->bh_data);
4048 dp->db_id = DATA_ID;
4049 dp->db_txt_start = dp->db_txt_end = page_count * mfp->mf_page_size;
4050 dp->db_free = dp->db_txt_start - HEADER_SIZE;
4051 dp->db_line_count = 0;
4052
4053 return hp;
4054}
4055
4056/*
4057 * create a new, empty, pointer block
4058 */
4059 static bhdr_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004060ml_new_ptr(memfile_T *mfp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004061{
4062 bhdr_T *hp;
4063 PTR_BL *pp;
4064
4065 if ((hp = mf_new(mfp, FALSE, 1)) == NULL)
4066 return NULL;
4067
4068 pp = (PTR_BL *)(hp->bh_data);
4069 pp->pb_id = PTR_ID;
4070 pp->pb_count = 0;
Bram Moolenaar20a825a2010-05-31 21:27:30 +02004071 pp->pb_count_max = (short_u)((mfp->mf_page_size - sizeof(PTR_BL))
4072 / sizeof(PTR_EN) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004073
4074 return hp;
4075}
4076
4077/*
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01004078 * Lookup line 'lnum' in a memline.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004079 *
4080 * action: if ML_DELETE or ML_INSERT the line count is updated while searching
4081 * if ML_FLUSH only flush a locked block
4082 * if ML_FIND just find the line
4083 *
4084 * If the block was found it is locked and put in ml_locked.
4085 * The stack is updated to lead to the locked block. The ip_high field in
4086 * the stack is updated to reflect the last line in the block AFTER the
4087 * insert or delete, also if the pointer block has not been updated yet. But
Bram Moolenaar6b803a72007-05-06 14:25:46 +00004088 * if ml_locked != NULL ml_locked_lineadd must be added to ip_high.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004089 *
4090 * return: NULL for failure, pointer to block header otherwise
4091 */
4092 static bhdr_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004093ml_find_line(buf_T *buf, linenr_T lnum, int action)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004094{
4095 DATA_BL *dp;
4096 PTR_BL *pp;
4097 infoptr_T *ip;
4098 bhdr_T *hp;
4099 memfile_T *mfp;
4100 linenr_T t;
4101 blocknr_T bnum, bnum2;
4102 int dirty;
4103 linenr_T low, high;
4104 int top;
4105 int page_count;
4106 int idx;
4107
4108 mfp = buf->b_ml.ml_mfp;
4109
4110 /*
4111 * If there is a locked block check if the wanted line is in it.
4112 * If not, flush and release the locked block.
4113 * Don't do this for ML_INSERT_SAME, because the stack need to be updated.
4114 * Don't do this for ML_FLUSH, because we want to flush the locked block.
Bram Moolenaar47b8b152007-02-07 02:41:57 +00004115 * Don't do this when 'swapfile' is reset, we want to load all the blocks.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004116 */
4117 if (buf->b_ml.ml_locked)
4118 {
Bram Moolenaar47b8b152007-02-07 02:41:57 +00004119 if (ML_SIMPLE(action)
4120 && buf->b_ml.ml_locked_low <= lnum
4121 && buf->b_ml.ml_locked_high >= lnum
4122 && !mf_dont_release)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004123 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004124 // remember to update pointer blocks and stack later
Bram Moolenaar071d4272004-06-13 20:20:40 +00004125 if (action == ML_INSERT)
4126 {
4127 ++(buf->b_ml.ml_locked_lineadd);
4128 ++(buf->b_ml.ml_locked_high);
4129 }
4130 else if (action == ML_DELETE)
4131 {
4132 --(buf->b_ml.ml_locked_lineadd);
4133 --(buf->b_ml.ml_locked_high);
4134 }
4135 return (buf->b_ml.ml_locked);
4136 }
4137
4138 mf_put(mfp, buf->b_ml.ml_locked, buf->b_ml.ml_flags & ML_LOCKED_DIRTY,
4139 buf->b_ml.ml_flags & ML_LOCKED_POS);
4140 buf->b_ml.ml_locked = NULL;
4141
Bram Moolenaar6b803a72007-05-06 14:25:46 +00004142 /*
4143 * If lines have been added or deleted in the locked block, need to
4144 * update the line count in pointer blocks.
4145 */
4146 if (buf->b_ml.ml_locked_lineadd != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004147 ml_lineadd(buf, buf->b_ml.ml_locked_lineadd);
4148 }
4149
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004150 if (action == ML_FLUSH) // nothing else to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00004151 return NULL;
4152
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004153 bnum = 1; // start at the root of the tree
Bram Moolenaar071d4272004-06-13 20:20:40 +00004154 page_count = 1;
4155 low = 1;
4156 high = buf->b_ml.ml_line_count;
4157
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004158 if (action == ML_FIND) // first try stack entries
Bram Moolenaar071d4272004-06-13 20:20:40 +00004159 {
4160 for (top = buf->b_ml.ml_stack_top - 1; top >= 0; --top)
4161 {
4162 ip = &(buf->b_ml.ml_stack[top]);
4163 if (ip->ip_low <= lnum && ip->ip_high >= lnum)
4164 {
4165 bnum = ip->ip_bnum;
4166 low = ip->ip_low;
4167 high = ip->ip_high;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004168 buf->b_ml.ml_stack_top = top; // truncate stack at prev entry
Bram Moolenaar071d4272004-06-13 20:20:40 +00004169 break;
4170 }
4171 }
4172 if (top < 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004173 buf->b_ml.ml_stack_top = 0; // not found, start at the root
Bram Moolenaar071d4272004-06-13 20:20:40 +00004174 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004175 else // ML_DELETE or ML_INSERT
4176 buf->b_ml.ml_stack_top = 0; // start at the root
Bram Moolenaar071d4272004-06-13 20:20:40 +00004177
4178/*
4179 * search downwards in the tree until a data block is found
4180 */
4181 for (;;)
4182 {
4183 if ((hp = mf_get(mfp, bnum, page_count)) == NULL)
4184 goto error_noblock;
4185
4186 /*
4187 * update high for insert/delete
4188 */
4189 if (action == ML_INSERT)
4190 ++high;
4191 else if (action == ML_DELETE)
4192 --high;
4193
4194 dp = (DATA_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004195 if (dp->db_id == DATA_ID) // data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00004196 {
4197 buf->b_ml.ml_locked = hp;
4198 buf->b_ml.ml_locked_low = low;
4199 buf->b_ml.ml_locked_high = high;
4200 buf->b_ml.ml_locked_lineadd = 0;
4201 buf->b_ml.ml_flags &= ~(ML_LOCKED_DIRTY | ML_LOCKED_POS);
4202 return hp;
4203 }
4204
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004205 pp = (PTR_BL *)(dp); // must be pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00004206 if (pp->pb_id != PTR_ID)
4207 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004208 iemsg(_("E317: pointer block id wrong"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004209 goto error_block;
4210 }
4211
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004212 if ((top = ml_add_stack(buf)) < 0) // add new entry to stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00004213 goto error_block;
4214 ip = &(buf->b_ml.ml_stack[top]);
4215 ip->ip_bnum = bnum;
4216 ip->ip_low = low;
4217 ip->ip_high = high;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004218 ip->ip_index = -1; // index not known yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00004219
4220 dirty = FALSE;
4221 for (idx = 0; idx < (int)pp->pb_count; ++idx)
4222 {
4223 t = pp->pb_pointer[idx].pe_line_count;
4224 CHECK(t == 0, _("pe_line_count is zero"));
4225 if ((low += t) > lnum)
4226 {
4227 ip->ip_index = idx;
4228 bnum = pp->pb_pointer[idx].pe_bnum;
4229 page_count = pp->pb_pointer[idx].pe_page_count;
4230 high = low - 1;
4231 low -= t;
4232
4233 /*
4234 * a negative block number may have been changed
4235 */
4236 if (bnum < 0)
4237 {
4238 bnum2 = mf_trans_del(mfp, bnum);
4239 if (bnum != bnum2)
4240 {
4241 bnum = bnum2;
4242 pp->pb_pointer[idx].pe_bnum = bnum;
4243 dirty = TRUE;
4244 }
4245 }
4246
4247 break;
4248 }
4249 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004250 if (idx >= (int)pp->pb_count) // past the end: something wrong!
Bram Moolenaar071d4272004-06-13 20:20:40 +00004251 {
4252 if (lnum > buf->b_ml.ml_line_count)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004253 siemsg(_("E322: line number out of range: %ld past the end"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00004254 lnum - buf->b_ml.ml_line_count);
4255
4256 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004257 siemsg(_("E323: line count wrong in block %ld"), bnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004258 goto error_block;
4259 }
4260 if (action == ML_DELETE)
4261 {
4262 pp->pb_pointer[idx].pe_line_count--;
4263 dirty = TRUE;
4264 }
4265 else if (action == ML_INSERT)
4266 {
4267 pp->pb_pointer[idx].pe_line_count++;
4268 dirty = TRUE;
4269 }
4270 mf_put(mfp, hp, dirty, FALSE);
4271 }
4272
4273error_block:
4274 mf_put(mfp, hp, FALSE, FALSE);
4275error_noblock:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02004276 /*
4277 * If action is ML_DELETE or ML_INSERT we have to correct the tree for
4278 * the incremented/decremented line counts, because there won't be a line
4279 * inserted/deleted after all.
4280 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004281 if (action == ML_DELETE)
4282 ml_lineadd(buf, 1);
4283 else if (action == ML_INSERT)
4284 ml_lineadd(buf, -1);
4285 buf->b_ml.ml_stack_top = 0;
4286 return NULL;
4287}
4288
4289/*
4290 * add an entry to the info pointer stack
4291 *
4292 * return -1 for failure, number of the new entry otherwise
4293 */
4294 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004295ml_add_stack(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004296{
4297 int top;
4298 infoptr_T *newstack;
4299
4300 top = buf->b_ml.ml_stack_top;
4301
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004302 // may have to increase the stack size
Bram Moolenaar071d4272004-06-13 20:20:40 +00004303 if (top == buf->b_ml.ml_stack_size)
4304 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004305 CHECK(top > 0, _("Stack size increases")); // more than 5 levels???
Bram Moolenaar071d4272004-06-13 20:20:40 +00004306
Bram Moolenaarc799fe22019-05-28 23:08:19 +02004307 newstack = ALLOC_MULT(infoptr_T, buf->b_ml.ml_stack_size + STACK_INCR);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004308 if (newstack == NULL)
4309 return -1;
Bram Moolenaarfbd302f2015-08-08 18:23:46 +02004310 if (top > 0)
4311 mch_memmove(newstack, buf->b_ml.ml_stack,
Bram Moolenaar8c8de832008-06-24 22:58:06 +00004312 (size_t)top * sizeof(infoptr_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004313 vim_free(buf->b_ml.ml_stack);
4314 buf->b_ml.ml_stack = newstack;
4315 buf->b_ml.ml_stack_size += STACK_INCR;
4316 }
4317
4318 buf->b_ml.ml_stack_top++;
4319 return top;
4320}
4321
4322/*
4323 * Update the pointer blocks on the stack for inserted/deleted lines.
4324 * The stack itself is also updated.
4325 *
4326 * When a insert/delete line action fails, the line is not inserted/deleted,
4327 * but the pointer blocks have already been updated. That is fixed here by
4328 * walking through the stack.
4329 *
4330 * Count is the number of lines added, negative if lines have been deleted.
4331 */
4332 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004333ml_lineadd(buf_T *buf, int count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004334{
4335 int idx;
4336 infoptr_T *ip;
4337 PTR_BL *pp;
4338 memfile_T *mfp = buf->b_ml.ml_mfp;
4339 bhdr_T *hp;
4340
4341 for (idx = buf->b_ml.ml_stack_top - 1; idx >= 0; --idx)
4342 {
4343 ip = &(buf->b_ml.ml_stack[idx]);
4344 if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
4345 break;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004346 pp = (PTR_BL *)(hp->bh_data); // must be pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00004347 if (pp->pb_id != PTR_ID)
4348 {
4349 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004350 iemsg(_("E317: pointer block id wrong 2"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004351 break;
4352 }
4353 pp->pb_pointer[ip->ip_index].pe_line_count += count;
4354 ip->ip_high += count;
4355 mf_put(mfp, hp, TRUE, FALSE);
4356 }
4357}
4358
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004359#if defined(HAVE_READLINK) || defined(PROTO)
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004360/*
4361 * Resolve a symlink in the last component of a file name.
4362 * Note that f_resolve() does it for every part of the path, we don't do that
4363 * here.
4364 * If it worked returns OK and the resolved link in "buf[MAXPATHL]".
4365 * Otherwise returns FAIL.
4366 */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004367 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004368resolve_symlink(char_u *fname, char_u *buf)
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004369{
4370 char_u tmp[MAXPATHL];
4371 int ret;
4372 int depth = 0;
4373
4374 if (fname == NULL)
4375 return FAIL;
4376
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004377 // Put the result so far in tmp[], starting with the original name.
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004378 vim_strncpy(tmp, fname, MAXPATHL - 1);
4379
4380 for (;;)
4381 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004382 // Limit symlink depth to 100, catch recursive loops.
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004383 if (++depth == 100)
4384 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004385 semsg(_("E773: Symlink loop for \"%s\""), fname);
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004386 return FAIL;
4387 }
4388
4389 ret = readlink((char *)tmp, (char *)buf, MAXPATHL - 1);
4390 if (ret <= 0)
4391 {
Bram Moolenaarcc984262005-12-23 22:19:46 +00004392 if (errno == EINVAL || errno == ENOENT)
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004393 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004394 // Found non-symlink or not existing file, stop here.
4395 // When at the first level use the unmodified name, skip the
4396 // call to vim_FullName().
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004397 if (depth == 1)
4398 return FAIL;
4399
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004400 // Use the resolved name in tmp[].
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004401 break;
4402 }
4403
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004404 // There must be some error reading links, use original name.
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004405 return FAIL;
4406 }
4407 buf[ret] = NUL;
4408
4409 /*
4410 * Check whether the symlink is relative or absolute.
4411 * If it's relative, build a new path based on the directory
4412 * portion of the filename (if any) and the path the symlink
4413 * points to.
4414 */
4415 if (mch_isFullName(buf))
4416 STRCPY(tmp, buf);
4417 else
4418 {
4419 char_u *tail;
4420
4421 tail = gettail(tmp);
4422 if (STRLEN(tail) + STRLEN(buf) >= MAXPATHL)
4423 return FAIL;
4424 STRCPY(tail, buf);
4425 }
4426 }
4427
4428 /*
4429 * Try to resolve the full name of the file so that the swapfile name will
4430 * be consistent even when opening a relative symlink from different
4431 * working directories.
4432 */
4433 return vim_FullName(tmp, buf, MAXPATHL, TRUE);
4434}
4435#endif
4436
Bram Moolenaar071d4272004-06-13 20:20:40 +00004437/*
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004438 * Make swap file name out of the file name and a directory name.
4439 * Returns pointer to allocated memory or NULL.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004440 */
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004441 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004442makeswapname(
4443 char_u *fname,
4444 char_u *ffname UNUSED,
4445 buf_T *buf,
4446 char_u *dir_name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004447{
4448 char_u *r, *s;
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02004449 char_u *fname_res = fname;
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004450#ifdef HAVE_READLINK
4451 char_u fname_buf[MAXPATHL];
Bram Moolenaar5966ea12020-07-15 15:30:05 +02004452
4453 // Expand symlink in the file name, so that we put the swap file with the
4454 // actual file instead of with the symlink.
4455 if (resolve_symlink(fname, fname_buf) == OK)
4456 fname_res = fname_buf;
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004457#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004458
Bram Moolenaar4f974752019-02-17 17:44:42 +01004459#if defined(UNIX) || defined(MSWIN) // Need _very_ long file names
Bram Moolenaarb113c3a2017-02-28 21:26:17 +01004460 int len = (int)STRLEN(dir_name);
Bram Moolenaarc525e3a2017-02-18 16:59:02 +01004461
4462 s = dir_name + len;
4463 if (after_pathsep(dir_name, s) && len > 1 && s[-1] == s[-2])
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004464 { // Ends with '//', Use Full path
Bram Moolenaar071d4272004-06-13 20:20:40 +00004465 r = NULL;
Bram Moolenaar5966ea12020-07-15 15:30:05 +02004466 if ((s = make_percent_swname(dir_name, fname_res)) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004467 {
4468 r = modname(s, (char_u *)".swp", FALSE);
4469 vim_free(s);
4470 }
4471 return r;
4472 }
4473#endif
4474
4475 r = buf_modname(
Bram Moolenaar071d4272004-06-13 20:20:40 +00004476 (buf->b_p_sn || buf->b_shortname),
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004477 fname_res,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004478 (char_u *)
Bram Moolenaare60acc12011-05-10 16:41:25 +02004479#if defined(VMS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004480 "_swp",
4481#else
4482 ".swp",
4483#endif
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004484 // Prepend a '.' to the swap file name for the current directory.
Bram Moolenaar48e330a2016-02-23 14:53:34 +01004485 dir_name[0] == '.' && dir_name[1] == NUL);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004486 if (r == NULL) // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00004487 return NULL;
4488
4489 s = get_file_in_dir(r, dir_name);
4490 vim_free(r);
4491 return s;
4492}
4493
4494/*
4495 * Get file name to use for swap file or backup file.
4496 * Use the name of the edited file "fname" and an entry in the 'dir' or 'bdir'
4497 * option "dname".
4498 * - If "dname" is ".", return "fname" (swap file in dir of file).
4499 * - If "dname" starts with "./", insert "dname" in "fname" (swap file
4500 * relative to dir of file).
4501 * - Otherwise, prepend "dname" to the tail of "fname" (swap file in specific
4502 * dir).
4503 *
4504 * The return value is an allocated string and can be NULL.
4505 */
4506 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004507get_file_in_dir(
4508 char_u *fname,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004509 char_u *dname) // don't use "dirname", it is a global for Alpha
Bram Moolenaar071d4272004-06-13 20:20:40 +00004510{
4511 char_u *t;
4512 char_u *tail;
4513 char_u *retval;
4514 int save_char;
4515
4516 tail = gettail(fname);
4517
4518 if (dname[0] == '.' && dname[1] == NUL)
4519 retval = vim_strsave(fname);
4520 else if (dname[0] == '.' && vim_ispathsep(dname[1]))
4521 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004522 if (tail == fname) // no path before file name
Bram Moolenaar071d4272004-06-13 20:20:40 +00004523 retval = concat_fnames(dname + 2, tail, TRUE);
4524 else
4525 {
4526 save_char = *tail;
4527 *tail = NUL;
4528 t = concat_fnames(fname, dname + 2, TRUE);
4529 *tail = save_char;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004530 if (t == NULL) // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00004531 retval = NULL;
4532 else
4533 {
4534 retval = concat_fnames(t, tail, TRUE);
4535 vim_free(t);
4536 }
4537 }
4538 }
4539 else
4540 retval = concat_fnames(dname, tail, TRUE);
4541
Bram Moolenaar4f974752019-02-17 17:44:42 +01004542#ifdef MSWIN
Bram Moolenaar69c35002013-11-04 02:54:12 +01004543 if (retval != NULL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01004544 for (t = gettail(retval); *t != NUL; MB_PTR_ADV(t))
Bram Moolenaar69c35002013-11-04 02:54:12 +01004545 if (*t == ':')
4546 *t = '%';
4547#endif
4548
Bram Moolenaar071d4272004-06-13 20:20:40 +00004549 return retval;
4550}
4551
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004552/*
4553 * Print the ATTENTION message: info about an existing swap file.
4554 */
4555 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004556attention_message(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004557 buf_T *buf, // buffer being edited
4558 char_u *fname) // swap file name
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004559{
Bram Moolenaar8767f522016-07-01 17:17:39 +02004560 stat_T st;
Bram Moolenaar63d25552019-05-10 21:28:38 +02004561 time_t swap_mtime;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004562
4563 ++no_wait_return;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004564 (void)emsg(_("E325: ATTENTION"));
Bram Moolenaar32526b32019-01-19 17:43:09 +01004565 msg_puts(_("\nFound a swap file by the name \""));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004566 msg_home_replace(fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004567 msg_puts("\"\n");
Bram Moolenaar63d25552019-05-10 21:28:38 +02004568 swap_mtime = swapfile_info(fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004569 msg_puts(_("While opening file \""));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004570 msg_outtrans(buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004571 msg_puts("\"\n");
Bram Moolenaard6105cb2018-10-13 19:06:27 +02004572 if (mch_stat((char *)buf->b_fname, &st) == -1)
4573 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004574 msg_puts(_(" CANNOT BE FOUND"));
Bram Moolenaard6105cb2018-10-13 19:06:27 +02004575 }
4576 else
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004577 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004578 msg_puts(_(" dated: "));
Bram Moolenaar63d25552019-05-10 21:28:38 +02004579 msg_puts(get_ctime(st.st_mtime, TRUE));
4580 if (swap_mtime != 0 && st.st_mtime > swap_mtime)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004581 msg_puts(_(" NEWER than swap file!\n"));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004582 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004583 // Some of these messages are long to allow translation to
4584 // other languages.
Bram Moolenaar32526b32019-01-19 17:43:09 +01004585 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"));
4586 msg_puts(_("(2) An edit session for this file crashed.\n"));
4587 msg_puts(_(" If this is the case, use \":recover\" or \"vim -r "));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004588 msg_outtrans(buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004589 msg_puts(_("\"\n to recover the changes (see \":help recovery\").\n"));
4590 msg_puts(_(" If you did this already, delete the swap file \""));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004591 msg_outtrans(fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004592 msg_puts(_("\"\n to avoid this message.\n"));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004593 cmdline_row = msg_row;
4594 --no_wait_return;
4595}
4596
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01004597#if defined(FEAT_EVAL)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004598/*
4599 * Trigger the SwapExists autocommands.
4600 * Returns a value for equivalent to do_dialog() (see below):
4601 * 0: still need to ask for a choice
4602 * 1: open read-only
4603 * 2: edit anyway
4604 * 3: recover
4605 * 4: delete it
4606 * 5: quit
4607 * 6: abort
4608 */
4609 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004610do_swapexists(buf_T *buf, char_u *fname)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004611{
4612 set_vim_var_string(VV_SWAPNAME, fname, -1);
4613 set_vim_var_string(VV_SWAPCHOICE, NULL, -1);
4614
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004615 // Trigger SwapExists autocommands with <afile> set to the file being
4616 // edited. Disallow changing directory here.
Bram Moolenaar12c22ce2009-04-22 13:58:46 +00004617 ++allbuf_lock;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004618 apply_autocmds(EVENT_SWAPEXISTS, buf->b_fname, NULL, FALSE, NULL);
Bram Moolenaar12c22ce2009-04-22 13:58:46 +00004619 --allbuf_lock;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004620
4621 set_vim_var_string(VV_SWAPNAME, NULL, -1);
4622
4623 switch (*get_vim_var_str(VV_SWAPCHOICE))
4624 {
4625 case 'o': return 1;
4626 case 'e': return 2;
4627 case 'r': return 3;
4628 case 'd': return 4;
4629 case 'q': return 5;
4630 case 'a': return 6;
4631 }
4632
4633 return 0;
4634}
4635#endif
4636
Bram Moolenaar071d4272004-06-13 20:20:40 +00004637/*
4638 * Find out what name to use for the swap file for buffer 'buf'.
4639 *
4640 * Several names are tried to find one that does not exist
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004641 * Returns the name in allocated memory or NULL.
Bram Moolenaarf541c362011-10-26 11:44:18 +02004642 * When out of memory "dirp" is set to NULL.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004643 *
4644 * Note: If BASENAMELEN is not correct, you will get error messages for
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004645 * not being able to open the swap or undo file
Bram Moolenaar12c22ce2009-04-22 13:58:46 +00004646 * Note: May trigger SwapExists autocmd, pointers may change!
Bram Moolenaar071d4272004-06-13 20:20:40 +00004647 */
4648 static char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004649findswapname(
4650 buf_T *buf,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004651 char_u **dirp, // pointer to list of directories
4652 char_u *old_fname) // don't give warning for this file name
Bram Moolenaar071d4272004-06-13 20:20:40 +00004653{
4654 char_u *fname;
4655 int n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004656 char_u *dir_name;
4657#ifdef AMIGA
4658 BPTR fh;
4659#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004660 int r;
Bram Moolenaar69c35002013-11-04 02:54:12 +01004661 char_u *buf_fname = buf->b_fname;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004662
Bram Moolenaar48e330a2016-02-23 14:53:34 +01004663#if !defined(UNIX)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004664# define CREATE_DUMMY_FILE
4665 FILE *dummyfd = NULL;
4666
Bram Moolenaar4f974752019-02-17 17:44:42 +01004667# ifdef MSWIN
Bram Moolenaar69c35002013-11-04 02:54:12 +01004668 if (buf_fname != NULL && !mch_isFullName(buf_fname)
4669 && vim_strchr(gettail(buf_fname), ':'))
4670 {
4671 char_u *t;
4672
4673 buf_fname = vim_strsave(buf_fname);
4674 if (buf_fname == NULL)
4675 buf_fname = buf->b_fname;
4676 else
Bram Moolenaar91acfff2017-03-12 19:22:36 +01004677 for (t = gettail(buf_fname); *t != NUL; MB_PTR_ADV(t))
Bram Moolenaar69c35002013-11-04 02:54:12 +01004678 if (*t == ':')
4679 *t = '%';
4680 }
4681# endif
4682
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004683 /*
4684 * If we start editing a new file, e.g. "test.doc", which resides on an
4685 * MSDOS compatible filesystem, it is possible that the file
4686 * "test.doc.swp" which we create will be exactly the same file. To avoid
4687 * this problem we temporarily create "test.doc". Don't do this when the
4688 * check below for a 8.3 file name is used.
4689 */
Bram Moolenaar69c35002013-11-04 02:54:12 +01004690 if (!(buf->b_p_sn || buf->b_shortname) && buf_fname != NULL
4691 && mch_getperm(buf_fname) < 0)
4692 dummyfd = mch_fopen((char *)buf_fname, "w");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004693#endif
4694
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004695 /*
4696 * Isolate a directory name from *dirp and put it in dir_name.
4697 * First allocate some memory to put the directory name in.
4698 */
Bram Moolenaar964b3742019-05-24 18:54:09 +02004699 dir_name = alloc(STRLEN(*dirp) + 1);
Bram Moolenaarf541c362011-10-26 11:44:18 +02004700 if (dir_name == NULL)
4701 *dirp = NULL;
4702 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004703 (void)copy_option_part(dirp, dir_name, 31000, ",");
4704
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004705 /*
4706 * we try different names until we find one that does not exist yet
4707 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004708 if (dir_name == NULL) // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00004709 fname = NULL;
4710 else
Bram Moolenaar69c35002013-11-04 02:54:12 +01004711 fname = makeswapname(buf_fname, buf->b_ffname, buf, dir_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004712
4713 for (;;)
4714 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004715 if (fname == NULL) // must be out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00004716 break;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004717 if ((n = (int)STRLEN(fname)) == 0) // safety check
Bram Moolenaar071d4272004-06-13 20:20:40 +00004718 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01004719 VIM_CLEAR(fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004720 break;
4721 }
Bram Moolenaar48e330a2016-02-23 14:53:34 +01004722#if defined(UNIX)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004723/*
4724 * Some systems have a MS-DOS compatible filesystem that use 8.3 character
4725 * file names. If this is the first try and the swap file name does not fit in
4726 * 8.3, detect if this is the case, set shortname and try again.
4727 */
4728 if (fname[n - 2] == 'w' && fname[n - 1] == 'p'
4729 && !(buf->b_p_sn || buf->b_shortname))
4730 {
4731 char_u *tail;
4732 char_u *fname2;
Bram Moolenaar8767f522016-07-01 17:17:39 +02004733 stat_T s1, s2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004734 int f1, f2;
4735 int created1 = FALSE, created2 = FALSE;
4736 int same = FALSE;
4737
4738 /*
4739 * Check if swapfile name does not fit in 8.3:
4740 * It either contains two dots, is longer than 8 chars, or starts
4741 * with a dot.
4742 */
Bram Moolenaar69c35002013-11-04 02:54:12 +01004743 tail = gettail(buf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004744 if ( vim_strchr(tail, '.') != NULL
4745 || STRLEN(tail) > (size_t)8
4746 || *gettail(fname) == '.')
4747 {
4748 fname2 = alloc(n + 2);
4749 if (fname2 != NULL)
4750 {
4751 STRCPY(fname2, fname);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004752 // if fname == "xx.xx.swp", fname2 = "xx.xx.swx"
4753 // if fname == ".xx.swp", fname2 = ".xx.swpx"
4754 // if fname == "123456789.swp", fname2 = "12345678x.swp"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004755 if (vim_strchr(tail, '.') != NULL)
4756 fname2[n - 1] = 'x';
4757 else if (*gettail(fname) == '.')
4758 {
4759 fname2[n] = 'x';
4760 fname2[n + 1] = NUL;
4761 }
4762 else
4763 fname2[n - 5] += 1;
4764 /*
4765 * may need to create the files to be able to use mch_stat()
4766 */
4767 f1 = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
4768 if (f1 < 0)
4769 {
4770 f1 = mch_open_rw((char *)fname,
4771 O_RDWR|O_CREAT|O_EXCL|O_EXTRA);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004772 created1 = TRUE;
4773 }
4774 if (f1 >= 0)
4775 {
4776 f2 = mch_open((char *)fname2, O_RDONLY | O_EXTRA, 0);
4777 if (f2 < 0)
4778 {
4779 f2 = mch_open_rw((char *)fname2,
4780 O_RDWR|O_CREAT|O_EXCL|O_EXTRA);
4781 created2 = TRUE;
4782 }
4783 if (f2 >= 0)
4784 {
4785 /*
4786 * Both files exist now. If mch_stat() returns the
4787 * same device and inode they are the same file.
4788 */
4789 if (mch_fstat(f1, &s1) != -1
4790 && mch_fstat(f2, &s2) != -1
4791 && s1.st_dev == s2.st_dev
4792 && s1.st_ino == s2.st_ino)
4793 same = TRUE;
4794 close(f2);
4795 if (created2)
4796 mch_remove(fname2);
4797 }
4798 close(f1);
4799 if (created1)
4800 mch_remove(fname);
4801 }
4802 vim_free(fname2);
4803 if (same)
4804 {
4805 buf->b_shortname = TRUE;
4806 vim_free(fname);
Bram Moolenaar69c35002013-11-04 02:54:12 +01004807 fname = makeswapname(buf_fname, buf->b_ffname,
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004808 buf, dir_name);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004809 continue; // try again with b_shortname set
Bram Moolenaar071d4272004-06-13 20:20:40 +00004810 }
4811 }
4812 }
4813 }
4814#endif
4815 /*
4816 * check if the swapfile already exists
4817 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004818 if (mch_getperm(fname) < 0) // it does not exist
Bram Moolenaar071d4272004-06-13 20:20:40 +00004819 {
4820#ifdef HAVE_LSTAT
Bram Moolenaar8767f522016-07-01 17:17:39 +02004821 stat_T sb;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004822
4823 /*
4824 * Extra security check: When a swap file is a symbolic link, this
4825 * is most likely a symlink attack.
4826 */
4827 if (mch_lstat((char *)fname, &sb) < 0)
4828#else
4829# ifdef AMIGA
4830 fh = Open((UBYTE *)fname, (long)MODE_NEWFILE);
4831 /*
4832 * on the Amiga mch_getperm() will return -1 when the file exists
4833 * but is being used by another program. This happens if you edit
4834 * a file twice.
4835 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004836 if (fh != (BPTR)NULL) // can open file, OK
Bram Moolenaar071d4272004-06-13 20:20:40 +00004837 {
4838 Close(fh);
4839 mch_remove(fname);
4840 break;
4841 }
4842 if (IoErr() != ERROR_OBJECT_IN_USE
4843 && IoErr() != ERROR_OBJECT_EXISTS)
4844# endif
4845#endif
4846 break;
4847 }
4848
4849 /*
4850 * A file name equal to old_fname is OK to use.
4851 */
4852 if (old_fname != NULL && fnamecmp(fname, old_fname) == 0)
4853 break;
4854
4855 /*
4856 * get here when file already exists
4857 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004858 if (fname[n - 2] == 'w' && fname[n - 1] == 'p') // first try
Bram Moolenaar071d4272004-06-13 20:20:40 +00004859 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00004860 /*
4861 * on MS-DOS compatible filesystems (e.g. messydos) file.doc.swp
4862 * and file.doc are the same file. To guess if this problem is
4863 * present try if file.doc.swx exists. If it does, we set
4864 * buf->b_shortname and try file_doc.swp (dots replaced by
4865 * underscores for this file), and try again. If it doesn't we
4866 * assume that "file.doc.swp" already exists.
4867 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004868 if (!(buf->b_p_sn || buf->b_shortname)) // not tried yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00004869 {
4870 fname[n - 1] = 'x';
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004871 r = mch_getperm(fname); // try "file.swx"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004872 fname[n - 1] = 'p';
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004873 if (r >= 0) // "file.swx" seems to exist
Bram Moolenaar071d4272004-06-13 20:20:40 +00004874 {
4875 buf->b_shortname = TRUE;
4876 vim_free(fname);
Bram Moolenaar69c35002013-11-04 02:54:12 +01004877 fname = makeswapname(buf_fname, buf->b_ffname,
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004878 buf, dir_name);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004879 continue; // try again with '.' replaced with '_'
Bram Moolenaar071d4272004-06-13 20:20:40 +00004880 }
4881 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004882 /*
4883 * If we get here the ".swp" file really exists.
4884 * Give an error message, unless recovering, no file name, we are
4885 * viewing a help file or when the path of the file is different
4886 * (happens when all .swp files are in one directory).
4887 */
Bram Moolenaar69c35002013-11-04 02:54:12 +01004888 if (!recoverymode && buf_fname != NULL
Bram Moolenaar2debf1c2019-08-04 20:44:19 +02004889 && !buf->b_help
4890 && !(buf->b_flags & (BF_DUMMY | BF_NO_SEA)))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004891 {
4892 int fd;
4893 struct block0 b0;
4894 int differ = FALSE;
4895
4896 /*
4897 * Try to read block 0 from the swap file to get the original
4898 * file name (and inode number).
4899 */
4900 fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
4901 if (fd >= 0)
4902 {
Bram Moolenaar540fc6f2010-12-17 16:27:16 +01004903 if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004904 {
4905 /*
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004906 * If the swapfile has the same directory as the
4907 * buffer don't compare the directory names, they can
4908 * have a different mountpoint.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004909 */
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004910 if (b0.b0_flags & B0_SAME_DIR)
4911 {
4912 if (fnamecmp(gettail(buf->b_ffname),
4913 gettail(b0.b0_fname)) != 0
4914 || !same_directory(fname, buf->b_ffname))
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004915 {
4916#ifdef CHECK_INODE
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004917 // Symlinks may point to the same file even
4918 // when the name differs, need to check the
4919 // inode too.
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004920 expand_env(b0.b0_fname, NameBuff, MAXPATHL);
4921 if (fnamecmp_ino(buf->b_ffname, NameBuff,
4922 char_to_long(b0.b0_ino)))
4923#endif
4924 differ = TRUE;
4925 }
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004926 }
4927 else
4928 {
4929 /*
4930 * The name in the swap file may be
4931 * "~user/path/file". Expand it first.
4932 */
4933 expand_env(b0.b0_fname, NameBuff, MAXPATHL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004934#ifdef CHECK_INODE
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004935 if (fnamecmp_ino(buf->b_ffname, NameBuff,
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004936 char_to_long(b0.b0_ino)))
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004937 differ = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004938#else
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004939 if (fnamecmp(NameBuff, buf->b_ffname) != 0)
4940 differ = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004941#endif
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004942 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004943 }
4944 close(fd);
4945 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004946
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004947 // give the ATTENTION message when there is an old swap file
4948 // for the current file, and the buffer was not recovered.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004949 if (differ == FALSE && !(curbuf->b_flags & BF_RECOVERED)
4950 && vim_strchr(p_shm, SHM_ATTENTION) == NULL)
4951 {
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004952 int choice = 0;
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02004953 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004954#ifdef CREATE_DUMMY_FILE
4955 int did_use_dummy = FALSE;
4956
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004957 // Avoid getting a warning for the file being created
4958 // outside of Vim, it was created at the start of this
4959 // function. Delete the file now, because Vim might exit
4960 // here if the window is closed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004961 if (dummyfd != NULL)
4962 {
4963 fclose(dummyfd);
4964 dummyfd = NULL;
Bram Moolenaar69c35002013-11-04 02:54:12 +01004965 mch_remove(buf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004966 did_use_dummy = TRUE;
4967 }
4968#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004969
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02004970#ifdef HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar071d4272004-06-13 20:20:40 +00004971 process_still_running = FALSE;
4972#endif
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02004973 // It's safe to delete the swap file if all these are true:
4974 // - the edited file exists
4975 // - the swap file has no changes and looks OK
4976 if (mch_stat((char *)buf->b_fname, &st) == 0
4977 && swapfile_unchanged(fname))
4978 {
4979 choice = 4;
4980 if (p_verbose > 0)
4981 verb_msg(_("Found a swap file that is not useful, deleting it"));
4982 }
4983
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01004984#if defined(FEAT_EVAL)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004985 /*
4986 * If there is an SwapExists autocommand and we can handle
4987 * the response, trigger it. It may return 0 to ask the
4988 * user anyway.
4989 */
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02004990 if (choice == 0
4991 && swap_exists_action != SEA_NONE
Bram Moolenaar69c35002013-11-04 02:54:12 +01004992 && has_autocmd(EVENT_SWAPEXISTS, buf_fname, buf))
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004993 choice = do_swapexists(buf, fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004994
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004995 if (choice == 0)
4996#endif
4997 {
4998#ifdef FEAT_GUI
Bram Moolenaar798184c2018-10-07 20:48:39 +02004999 // If we are supposed to start the GUI but it wasn't
5000 // completely started yet, start it now. This makes
5001 // the messages displayed in the Vim window when
5002 // loading a session from the .gvimrc file.
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005003 if (gui.starting && !gui.in_use)
Bram Moolenaarafde13b2019-04-28 19:46:49 +02005004 gui_start(NULL);
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005005#endif
Bram Moolenaar798184c2018-10-07 20:48:39 +02005006 // Show info about the existing swap file.
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005007 attention_message(buf, fname);
5008
Bram Moolenaar798184c2018-10-07 20:48:39 +02005009 // We don't want a 'q' typed at the more-prompt
5010 // interrupt loading a file.
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005011 got_int = FALSE;
Bram Moolenaar798184c2018-10-07 20:48:39 +02005012
5013 // If vimrc has "simalt ~x" we don't want it to
5014 // interfere with the prompt here.
Bram Moolenaar6a2633b2018-10-07 23:16:36 +02005015 flush_buffers(FLUSH_TYPEAHEAD);
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005016 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005017
5018#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005019 if (swap_exists_action != SEA_NONE && choice == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005020 {
5021 char_u *name;
5022
Bram Moolenaar964b3742019-05-24 18:54:09 +02005023 name = alloc(STRLEN(fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005024 + STRLEN(_("Swap file \""))
Bram Moolenaar964b3742019-05-24 18:54:09 +02005025 + STRLEN(_("\" already exists!")) + 5);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005026 if (name != NULL)
5027 {
5028 STRCPY(name, _("Swap file \""));
5029 home_replace(NULL, fname, name + STRLEN(name),
5030 1000, TRUE);
5031 STRCAT(name, _("\" already exists!"));
5032 }
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005033 choice = do_dialog(VIM_WARNING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005034 (char_u *)_("VIM - ATTENTION"),
5035 name == NULL
5036 ? (char_u *)_("Swap file already exists!")
5037 : name,
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02005038# ifdef HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar071d4272004-06-13 20:20:40 +00005039 process_still_running
5040 ? (char_u *)_("&Open Read-Only\n&Edit anyway\n&Recover\n&Quit\n&Abort") :
5041# endif
Bram Moolenaard2c340a2011-01-17 20:08:11 +01005042 (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 +00005043
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02005044# ifdef HAVE_PROCESS_STILL_RUNNING
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005045 if (process_still_running && choice >= 4)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005046 choice++; // Skip missing "Delete it" button
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005047# endif
5048 vim_free(name);
5049
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005050 // pretend screen didn't scroll, need redraw anyway
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005051 msg_scrolled = 0;
5052 redraw_all_later(NOT_VALID);
5053 }
5054#endif
5055
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005056 if (choice > 0)
5057 {
5058 switch (choice)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005059 {
5060 case 1:
5061 buf->b_p_ro = TRUE;
5062 break;
5063 case 2:
5064 break;
5065 case 3:
5066 swap_exists_action = SEA_RECOVER;
5067 break;
5068 case 4:
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005069 mch_remove(fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005070 break;
5071 case 5:
5072 swap_exists_action = SEA_QUIT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005073 break;
5074 case 6:
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00005075 swap_exists_action = SEA_QUIT;
5076 got_int = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005077 break;
5078 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005079
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005080 // If the file was deleted this fname can be used.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005081 if (mch_getperm(fname) < 0)
5082 break;
5083 }
5084 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005085 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01005086 msg_puts("\n");
Bram Moolenaar4770d092006-01-12 23:22:24 +00005087 if (msg_silent == 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005088 // call wait_return() later
Bram Moolenaar4770d092006-01-12 23:22:24 +00005089 need_wait_return = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005090 }
5091
5092#ifdef CREATE_DUMMY_FILE
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005093 // Going to try another name, need the dummy file again.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005094 if (did_use_dummy)
Bram Moolenaar69c35002013-11-04 02:54:12 +01005095 dummyfd = mch_fopen((char *)buf_fname, "w");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005096#endif
5097 }
5098 }
5099 }
5100
5101 /*
5102 * Change the ".swp" extension to find another file that can be used.
5103 * First decrement the last char: ".swo", ".swn", etc.
5104 * If that still isn't enough decrement the last but one char: ".svz"
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00005105 * Can happen when editing many "No Name" buffers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005106 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005107 if (fname[n - 1] == 'a') // ".s?a"
Bram Moolenaar071d4272004-06-13 20:20:40 +00005108 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005109 if (fname[n - 2] == 'a') // ".saa": tried enough, give up
Bram Moolenaar071d4272004-06-13 20:20:40 +00005110 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005111 emsg(_("E326: Too many swap files found"));
Bram Moolenaard23a8232018-02-10 18:45:26 +01005112 VIM_CLEAR(fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005113 break;
5114 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005115 --fname[n - 2]; // ".svz", ".suz", etc.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005116 fname[n - 1] = 'z' + 1;
5117 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005118 --fname[n - 1]; // ".swo", ".swn", etc.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005119 }
5120
5121 vim_free(dir_name);
5122#ifdef CREATE_DUMMY_FILE
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005123 if (dummyfd != NULL) // file has been created temporarily
Bram Moolenaar071d4272004-06-13 20:20:40 +00005124 {
5125 fclose(dummyfd);
Bram Moolenaar69c35002013-11-04 02:54:12 +01005126 mch_remove(buf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005127 }
5128#endif
Bram Moolenaar4f974752019-02-17 17:44:42 +01005129#ifdef MSWIN
Bram Moolenaar69c35002013-11-04 02:54:12 +01005130 if (buf_fname != buf->b_fname)
5131 vim_free(buf_fname);
5132#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005133 return fname;
5134}
5135
5136 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005137b0_magic_wrong(ZERO_BL *b0p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005138{
5139 return (b0p->b0_magic_long != (long)B0_MAGIC_LONG
5140 || b0p->b0_magic_int != (int)B0_MAGIC_INT
5141 || b0p->b0_magic_short != (short)B0_MAGIC_SHORT
5142 || b0p->b0_magic_char != B0_MAGIC_CHAR);
5143}
5144
5145#ifdef CHECK_INODE
5146/*
5147 * Compare current file name with file name from swap file.
5148 * Try to use inode numbers when possible.
5149 * Return non-zero when files are different.
5150 *
5151 * When comparing file names a few things have to be taken into consideration:
5152 * - When working over a network the full path of a file depends on the host.
5153 * We check the inode number if possible. It is not 100% reliable though,
5154 * because the device number cannot be used over a network.
5155 * - When a file does not exist yet (editing a new file) there is no inode
5156 * number.
5157 * - The file name in a swap file may not be valid on the current host. The
5158 * "~user" form is used whenever possible to avoid this.
5159 *
5160 * This is getting complicated, let's make a table:
5161 *
5162 * ino_c ino_s fname_c fname_s differ =
5163 *
5164 * both files exist -> compare inode numbers:
5165 * != 0 != 0 X X ino_c != ino_s
5166 *
5167 * inode number(s) unknown, file names available -> compare file names
5168 * == 0 X OK OK fname_c != fname_s
5169 * X == 0 OK OK fname_c != fname_s
5170 *
5171 * current file doesn't exist, file for swap file exist, file name(s) not
5172 * available -> probably different
5173 * == 0 != 0 FAIL X TRUE
5174 * == 0 != 0 X FAIL TRUE
5175 *
5176 * current file exists, inode for swap unknown, file name(s) not
5177 * available -> probably different
5178 * != 0 == 0 FAIL X TRUE
5179 * != 0 == 0 X FAIL TRUE
5180 *
5181 * current file doesn't exist, inode for swap unknown, one file name not
5182 * available -> probably different
5183 * == 0 == 0 FAIL OK TRUE
5184 * == 0 == 0 OK FAIL TRUE
5185 *
5186 * current file doesn't exist, inode for swap unknown, both file names not
Bram Moolenaar8c3169c2018-05-12 17:04:12 +02005187 * available -> compare file names
5188 * == 0 == 0 FAIL FAIL fname_c != fname_s
Bram Moolenaar071d4272004-06-13 20:20:40 +00005189 *
5190 * Note that when the ino_t is 64 bits, only the last 32 will be used. This
5191 * can't be changed without making the block 0 incompatible with 32 bit
5192 * versions.
5193 */
5194
5195 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005196fnamecmp_ino(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005197 char_u *fname_c, // current file name
5198 char_u *fname_s, // file name from swap file
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005199 long ino_block0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005200{
Bram Moolenaar8767f522016-07-01 17:17:39 +02005201 stat_T st;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005202 ino_t ino_c = 0; // ino of current file
5203 ino_t ino_s; // ino of file from swap file
5204 char_u buf_c[MAXPATHL]; // full path of fname_c
5205 char_u buf_s[MAXPATHL]; // full path of fname_s
5206 int retval_c; // flag: buf_c valid
5207 int retval_s; // flag: buf_s valid
Bram Moolenaar071d4272004-06-13 20:20:40 +00005208
5209 if (mch_stat((char *)fname_c, &st) == 0)
5210 ino_c = (ino_t)st.st_ino;
5211
5212 /*
5213 * First we try to get the inode from the file name, because the inode in
5214 * the swap file may be outdated. If that fails (e.g. this path is not
5215 * valid on this machine), use the inode from block 0.
5216 */
5217 if (mch_stat((char *)fname_s, &st) == 0)
5218 ino_s = (ino_t)st.st_ino;
5219 else
5220 ino_s = (ino_t)ino_block0;
5221
5222 if (ino_c && ino_s)
5223 return (ino_c != ino_s);
5224
5225 /*
5226 * One of the inode numbers is unknown, try a forced vim_FullName() and
5227 * compare the file names.
5228 */
5229 retval_c = vim_FullName(fname_c, buf_c, MAXPATHL, TRUE);
5230 retval_s = vim_FullName(fname_s, buf_s, MAXPATHL, TRUE);
5231 if (retval_c == OK && retval_s == OK)
Bram Moolenaar8c3169c2018-05-12 17:04:12 +02005232 return STRCMP(buf_c, buf_s) != 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005233
5234 /*
5235 * Can't compare inodes or file names, guess that the files are different,
Bram Moolenaar8c3169c2018-05-12 17:04:12 +02005236 * unless both appear not to exist at all, then compare with the file name
5237 * in the swap file.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005238 */
5239 if (ino_s == 0 && ino_c == 0 && retval_c == FAIL && retval_s == FAIL)
Bram Moolenaar8c3169c2018-05-12 17:04:12 +02005240 return STRCMP(fname_c, fname_s) != 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005241 return TRUE;
5242}
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005243#endif // CHECK_INODE
Bram Moolenaar071d4272004-06-13 20:20:40 +00005244
5245/*
5246 * Move a long integer into a four byte character array.
5247 * Used for machine independency in block zero.
5248 */
5249 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005250long_to_char(long n, char_u *s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005251{
5252 s[0] = (char_u)(n & 0xff);
5253 n = (unsigned)n >> 8;
5254 s[1] = (char_u)(n & 0xff);
5255 n = (unsigned)n >> 8;
5256 s[2] = (char_u)(n & 0xff);
5257 n = (unsigned)n >> 8;
5258 s[3] = (char_u)(n & 0xff);
5259}
5260
5261 static long
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005262char_to_long(char_u *s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005263{
5264 long retval;
5265
5266 retval = s[3];
5267 retval <<= 8;
5268 retval |= s[2];
5269 retval <<= 8;
5270 retval |= s[1];
5271 retval <<= 8;
5272 retval |= s[0];
5273
5274 return retval;
5275}
5276
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005277/*
5278 * Set the flags in the first block of the swap file:
5279 * - file is modified or not: buf->b_changed
5280 * - 'fileformat'
5281 * - 'fileencoding'
5282 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005283 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005284ml_setflags(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005285{
5286 bhdr_T *hp;
5287 ZERO_BL *b0p;
5288
5289 if (!buf->b_ml.ml_mfp)
5290 return;
5291 for (hp = buf->b_ml.ml_mfp->mf_used_last; hp != NULL; hp = hp->bh_prev)
5292 {
5293 if (hp->bh_bnum == 0)
5294 {
5295 b0p = (ZERO_BL *)(hp->bh_data);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005296 b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0;
5297 b0p->b0_flags = (b0p->b0_flags & ~B0_FF_MASK)
5298 | (get_fileformat(buf) + 1);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005299 add_b0_fenc(b0p, buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005300 hp->bh_flags |= BH_DIRTY;
5301 mf_sync(buf->b_ml.ml_mfp, MFS_ZERO);
5302 break;
5303 }
5304 }
5305}
5306
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005307#if defined(FEAT_CRYPT) || defined(PROTO)
5308/*
5309 * If "data" points to a data block encrypt the text in it and return a copy
5310 * in allocated memory. Return NULL when out of memory.
5311 * Otherwise return "data".
5312 */
5313 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005314ml_encrypt_data(
5315 memfile_T *mfp,
5316 char_u *data,
Bram Moolenaar8767f522016-07-01 17:17:39 +02005317 off_T offset,
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005318 unsigned size)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005319{
5320 DATA_BL *dp = (DATA_BL *)data;
5321 char_u *head_end;
5322 char_u *text_start;
5323 char_u *new_data;
5324 int text_len;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005325 cryptstate_T *state;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005326
5327 if (dp->db_id != DATA_ID)
5328 return data;
5329
Bram Moolenaarbc563362015-06-09 18:35:25 +02005330 state = ml_crypt_prepare(mfp, offset, FALSE);
5331 if (state == NULL)
5332 return data;
5333
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005334 new_data = alloc(size);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005335 if (new_data == NULL)
5336 return NULL;
5337 head_end = (char_u *)(&dp->db_index[dp->db_line_count]);
5338 text_start = (char_u *)dp + dp->db_txt_start;
5339 text_len = size - dp->db_txt_start;
5340
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005341 // Copy the header and the text.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005342 mch_memmove(new_data, dp, head_end - (char_u *)dp);
5343
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005344 // Encrypt the text.
Christian Brabandtf573c6e2021-06-20 14:02:16 +02005345 crypt_encode(state, text_start, text_len, new_data + dp->db_txt_start,
5346 FALSE);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005347 crypt_free_state(state);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005348
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005349 // Clear the gap.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005350 if (head_end < text_start)
5351 vim_memset(new_data + (head_end - data), 0, text_start - head_end);
5352
5353 return new_data;
5354}
5355
5356/*
Bram Moolenaarbc563362015-06-09 18:35:25 +02005357 * Decrypt the text in "data" if it points to an encrypted data block.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005358 */
5359 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005360ml_decrypt_data(
5361 memfile_T *mfp,
5362 char_u *data,
Bram Moolenaar8767f522016-07-01 17:17:39 +02005363 off_T offset,
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005364 unsigned size)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005365{
5366 DATA_BL *dp = (DATA_BL *)data;
5367 char_u *head_end;
5368 char_u *text_start;
5369 int text_len;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005370 cryptstate_T *state;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005371
5372 if (dp->db_id == DATA_ID)
5373 {
5374 head_end = (char_u *)(&dp->db_index[dp->db_line_count]);
5375 text_start = (char_u *)dp + dp->db_txt_start;
5376 text_len = dp->db_txt_end - dp->db_txt_start;
5377
5378 if (head_end > text_start || dp->db_txt_start > size
5379 || dp->db_txt_end > size)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005380 return; // data was messed up
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005381
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005382 state = ml_crypt_prepare(mfp, offset, TRUE);
Bram Moolenaarbc563362015-06-09 18:35:25 +02005383 if (state != NULL)
5384 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005385 // Decrypt the text in place.
Christian Brabandtf573c6e2021-06-20 14:02:16 +02005386 crypt_decode_inplace(state, text_start, text_len, FALSE);
Bram Moolenaarbc563362015-06-09 18:35:25 +02005387 crypt_free_state(state);
5388 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005389 }
5390}
5391
5392/*
5393 * Prepare for encryption/decryption, using the key, seed and offset.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005394 * Return an allocated cryptstate_T *.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005395 */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005396 static cryptstate_T *
Bram Moolenaar8767f522016-07-01 17:17:39 +02005397ml_crypt_prepare(memfile_T *mfp, off_T offset, int reading)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005398{
5399 buf_T *buf = mfp->mf_buffer;
5400 char_u salt[50];
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005401 int method_nr;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005402 char_u *key;
5403 char_u *seed;
5404
5405 if (reading && mfp->mf_old_key != NULL)
5406 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005407 // Reading back blocks with the previous key/method/seed.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005408 method_nr = mfp->mf_old_cm;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005409 key = mfp->mf_old_key;
5410 seed = mfp->mf_old_seed;
5411 }
5412 else
5413 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005414 method_nr = crypt_get_method_nr(buf);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005415 key = buf->b_p_key;
5416 seed = mfp->mf_seed;
5417 }
Bram Moolenaarbc563362015-06-09 18:35:25 +02005418 if (*key == NUL)
5419 return NULL;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005420
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005421 if (method_nr == CRYPT_M_ZIP)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005422 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005423 // For PKzip: Append the offset to the key, so that we use a different
5424 // key for every block.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005425 vim_snprintf((char *)salt, sizeof(salt), "%s%ld", key, (long)offset);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005426 return crypt_create(method_nr, salt, NULL, 0, NULL, 0);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005427 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005428
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005429 // Using blowfish or better: add salt and seed. We use the byte offset
5430 // of the block for the salt.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005431 vim_snprintf((char *)salt, sizeof(salt), "%ld", (long)offset);
5432 return crypt_create(method_nr, key, salt, (int)STRLEN(salt),
Christian Brabandtf573c6e2021-06-20 14:02:16 +02005433 seed, MF_SEED_LEN);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005434}
5435
5436#endif
5437
5438
Bram Moolenaar071d4272004-06-13 20:20:40 +00005439#if defined(FEAT_BYTEOFF) || defined(PROTO)
5440
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005441#define MLCS_MAXL 800 // max no of lines in chunk
5442#define MLCS_MINL 400 // should be half of MLCS_MAXL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005443
5444/*
Bram Moolenaar0ad014c2010-07-25 14:00:46 +02005445 * Keep information for finding byte offset of a line, updtype may be one of:
Bram Moolenaar071d4272004-06-13 20:20:40 +00005446 * ML_CHNK_ADDLINE: Add len to parent chunk, possibly splitting it
5447 * Careful: ML_CHNK_ADDLINE may cause ml_find_line() to be called.
5448 * ML_CHNK_DELLINE: Subtract len from parent chunk, possibly deleting it
5449 * ML_CHNK_UPDLINE: Add len to parent chunk, as a signed entity.
5450 */
5451 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005452ml_updatechunk(
5453 buf_T *buf,
5454 linenr_T line,
5455 long len,
5456 int updtype)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005457{
5458 static buf_T *ml_upd_lastbuf = NULL;
5459 static linenr_T ml_upd_lastline;
5460 static linenr_T ml_upd_lastcurline;
5461 static int ml_upd_lastcurix;
5462
5463 linenr_T curline = ml_upd_lastcurline;
5464 int curix = ml_upd_lastcurix;
5465 long size;
5466 chunksize_T *curchnk;
5467 int rest;
5468 bhdr_T *hp;
5469 DATA_BL *dp;
5470
5471 if (buf->b_ml.ml_usedchunks == -1 || len == 0)
5472 return;
5473 if (buf->b_ml.ml_chunksize == NULL)
5474 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005475 buf->b_ml.ml_chunksize = ALLOC_MULT(chunksize_T, 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005476 if (buf->b_ml.ml_chunksize == NULL)
5477 {
5478 buf->b_ml.ml_usedchunks = -1;
5479 return;
5480 }
5481 buf->b_ml.ml_numchunks = 100;
5482 buf->b_ml.ml_usedchunks = 1;
5483 buf->b_ml.ml_chunksize[0].mlcs_numlines = 1;
5484 buf->b_ml.ml_chunksize[0].mlcs_totalsize = 1;
5485 }
5486
5487 if (updtype == ML_CHNK_UPDLINE && buf->b_ml.ml_line_count == 1)
5488 {
5489 /*
5490 * First line in empty buffer from ml_flush_line() -- reset
5491 */
5492 buf->b_ml.ml_usedchunks = 1;
5493 buf->b_ml.ml_chunksize[0].mlcs_numlines = 1;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01005494 buf->b_ml.ml_chunksize[0].mlcs_totalsize = (long)buf->b_ml.ml_line_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005495 return;
5496 }
5497
5498 /*
5499 * Find chunk that our line belongs to, curline will be at start of the
5500 * chunk.
5501 */
5502 if (buf != ml_upd_lastbuf || line != ml_upd_lastline + 1
5503 || updtype != ML_CHNK_ADDLINE)
5504 {
5505 for (curline = 1, curix = 0;
5506 curix < buf->b_ml.ml_usedchunks - 1
5507 && line >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines;
5508 curix++)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005509 curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005510 }
Bram Moolenaara9a8e042018-10-30 22:15:55 +01005511 else if (curix < buf->b_ml.ml_usedchunks - 1
5512 && line >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005513 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005514 // Adjust cached curix & curline
Bram Moolenaar071d4272004-06-13 20:20:40 +00005515 curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
5516 curix++;
5517 }
5518 curchnk = buf->b_ml.ml_chunksize + curix;
5519
5520 if (updtype == ML_CHNK_DELLINE)
Bram Moolenaar5a6404c2006-11-01 17:12:57 +00005521 len = -len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005522 curchnk->mlcs_totalsize += len;
5523 if (updtype == ML_CHNK_ADDLINE)
5524 {
5525 curchnk->mlcs_numlines++;
5526
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005527 // May resize here so we don't have to do it in both cases below
Bram Moolenaar071d4272004-06-13 20:20:40 +00005528 if (buf->b_ml.ml_usedchunks + 1 >= buf->b_ml.ml_numchunks)
5529 {
Bram Moolenaar9abd5c62015-02-10 18:34:01 +01005530 chunksize_T *t_chunksize = buf->b_ml.ml_chunksize;
5531
Bram Moolenaar071d4272004-06-13 20:20:40 +00005532 buf->b_ml.ml_numchunks = buf->b_ml.ml_numchunks * 3 / 2;
Bram Moolenaar51b6eb42020-08-22 15:19:18 +02005533 buf->b_ml.ml_chunksize = vim_realloc(buf->b_ml.ml_chunksize,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005534 sizeof(chunksize_T) * buf->b_ml.ml_numchunks);
5535 if (buf->b_ml.ml_chunksize == NULL)
5536 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005537 // Hmmmm, Give up on offset for this buffer
Bram Moolenaar9abd5c62015-02-10 18:34:01 +01005538 vim_free(t_chunksize);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005539 buf->b_ml.ml_usedchunks = -1;
5540 return;
5541 }
5542 }
5543
5544 if (buf->b_ml.ml_chunksize[curix].mlcs_numlines >= MLCS_MAXL)
5545 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005546 int count; // number of entries in block
Bram Moolenaar071d4272004-06-13 20:20:40 +00005547 int idx;
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005548 int end_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005549 int text_end;
5550 int linecnt;
5551
5552 mch_memmove(buf->b_ml.ml_chunksize + curix + 1,
5553 buf->b_ml.ml_chunksize + curix,
5554 (buf->b_ml.ml_usedchunks - curix) *
5555 sizeof(chunksize_T));
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005556 // Compute length of first half of lines in the split chunk
Bram Moolenaar071d4272004-06-13 20:20:40 +00005557 size = 0;
5558 linecnt = 0;
5559 while (curline < buf->b_ml.ml_line_count
5560 && linecnt < MLCS_MINL)
5561 {
5562 if ((hp = ml_find_line(buf, curline, ML_FIND)) == NULL)
5563 {
5564 buf->b_ml.ml_usedchunks = -1;
5565 return;
5566 }
5567 dp = (DATA_BL *)(hp->bh_data);
5568 count = (long)(buf->b_ml.ml_locked_high) -
5569 (long)(buf->b_ml.ml_locked_low) + 1;
5570 idx = curline - buf->b_ml.ml_locked_low;
5571 curline = buf->b_ml.ml_locked_high + 1;
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005572
5573 // compute index of last line to use in this MEMLINE
Bram Moolenaar071d4272004-06-13 20:20:40 +00005574 rest = count - idx;
5575 if (linecnt + rest > MLCS_MINL)
5576 {
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005577 end_idx = idx + MLCS_MINL - linecnt - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005578 linecnt = MLCS_MINL;
5579 }
5580 else
5581 {
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005582 end_idx = count - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005583 linecnt += rest;
5584 }
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01005585#ifdef FEAT_PROP_POPUP
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005586 if (buf->b_has_textprop)
5587 {
5588 int i;
5589
5590 // We cannot use the text pointers to get the text length,
5591 // the text prop info would also be counted. Go over the
5592 // lines.
5593 for (i = end_idx; i < idx; ++i)
Bram Moolenaar4b7214e2019-01-03 21:55:32 +01005594 size += (int)STRLEN((char_u *)dp + (dp->db_index[i] & DB_INDEX_MASK)) + 1;
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005595 }
5596 else
5597#endif
5598 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005599 if (idx == 0)// first line in block, text at the end
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005600 text_end = dp->db_txt_end;
5601 else
5602 text_end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK);
5603 size += text_end - ((dp->db_index[end_idx]) & DB_INDEX_MASK);
5604 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005605 }
5606 buf->b_ml.ml_chunksize[curix].mlcs_numlines = linecnt;
5607 buf->b_ml.ml_chunksize[curix + 1].mlcs_numlines -= linecnt;
5608 buf->b_ml.ml_chunksize[curix].mlcs_totalsize = size;
5609 buf->b_ml.ml_chunksize[curix + 1].mlcs_totalsize -= size;
5610 buf->b_ml.ml_usedchunks++;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005611 ml_upd_lastbuf = NULL; // Force recalc of curix & curline
Bram Moolenaar071d4272004-06-13 20:20:40 +00005612 return;
5613 }
5614 else if (buf->b_ml.ml_chunksize[curix].mlcs_numlines >= MLCS_MINL
5615 && curix == buf->b_ml.ml_usedchunks - 1
5616 && buf->b_ml.ml_line_count - line <= 1)
5617 {
5618 /*
Bram Moolenaar8e7d6222020-12-18 19:49:56 +01005619 * We are in the last chunk and it is cheap to create a new one
Bram Moolenaar071d4272004-06-13 20:20:40 +00005620 * after this. Do it now to avoid the loop above later on
5621 */
5622 curchnk = buf->b_ml.ml_chunksize + curix + 1;
5623 buf->b_ml.ml_usedchunks++;
5624 if (line == buf->b_ml.ml_line_count)
5625 {
5626 curchnk->mlcs_numlines = 0;
5627 curchnk->mlcs_totalsize = 0;
5628 }
5629 else
5630 {
5631 /*
5632 * Line is just prior to last, move count for last
5633 * This is the common case when loading a new file
5634 */
5635 hp = ml_find_line(buf, buf->b_ml.ml_line_count, ML_FIND);
5636 if (hp == NULL)
5637 {
5638 buf->b_ml.ml_usedchunks = -1;
5639 return;
5640 }
5641 dp = (DATA_BL *)(hp->bh_data);
5642 if (dp->db_line_count == 1)
5643 rest = dp->db_txt_end - dp->db_txt_start;
5644 else
5645 rest =
5646 ((dp->db_index[dp->db_line_count - 2]) & DB_INDEX_MASK)
5647 - dp->db_txt_start;
5648 curchnk->mlcs_totalsize = rest;
5649 curchnk->mlcs_numlines = 1;
5650 curchnk[-1].mlcs_totalsize -= rest;
5651 curchnk[-1].mlcs_numlines -= 1;
5652 }
5653 }
5654 }
5655 else if (updtype == ML_CHNK_DELLINE)
5656 {
5657 curchnk->mlcs_numlines--;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005658 ml_upd_lastbuf = NULL; // Force recalc of curix & curline
Bram Moolenaar071d4272004-06-13 20:20:40 +00005659 if (curix < (buf->b_ml.ml_usedchunks - 1)
5660 && (curchnk->mlcs_numlines + curchnk[1].mlcs_numlines)
5661 <= MLCS_MINL)
5662 {
5663 curix++;
5664 curchnk = buf->b_ml.ml_chunksize + curix;
5665 }
5666 else if (curix == 0 && curchnk->mlcs_numlines <= 0)
5667 {
5668 buf->b_ml.ml_usedchunks--;
5669 mch_memmove(buf->b_ml.ml_chunksize, buf->b_ml.ml_chunksize + 1,
5670 buf->b_ml.ml_usedchunks * sizeof(chunksize_T));
5671 return;
5672 }
5673 else if (curix == 0 || (curchnk->mlcs_numlines > 10
5674 && (curchnk->mlcs_numlines + curchnk[-1].mlcs_numlines)
5675 > MLCS_MINL))
5676 {
5677 return;
5678 }
5679
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005680 // Collapse chunks
Bram Moolenaar071d4272004-06-13 20:20:40 +00005681 curchnk[-1].mlcs_numlines += curchnk->mlcs_numlines;
5682 curchnk[-1].mlcs_totalsize += curchnk->mlcs_totalsize;
5683 buf->b_ml.ml_usedchunks--;
5684 if (curix < buf->b_ml.ml_usedchunks)
5685 {
5686 mch_memmove(buf->b_ml.ml_chunksize + curix,
5687 buf->b_ml.ml_chunksize + curix + 1,
5688 (buf->b_ml.ml_usedchunks - curix) *
5689 sizeof(chunksize_T));
5690 }
5691 return;
5692 }
5693 ml_upd_lastbuf = buf;
5694 ml_upd_lastline = line;
5695 ml_upd_lastcurline = curline;
5696 ml_upd_lastcurix = curix;
5697}
5698
5699/*
5700 * Find offset for line or line with offset.
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005701 * Find line with offset if "lnum" is 0; return remaining offset in offp
5702 * Find offset of line if "lnum" > 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00005703 * return -1 if information is not available
5704 */
5705 long
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005706ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005707{
5708 linenr_T curline;
5709 int curix;
5710 long size;
5711 bhdr_T *hp;
5712 DATA_BL *dp;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005713 int count; // number of entries in block
Bram Moolenaar071d4272004-06-13 20:20:40 +00005714 int idx;
5715 int start_idx;
5716 int text_end;
5717 long offset;
5718 int len;
5719 int ffdos = (get_fileformat(buf) == EOL_DOS);
5720 int extra = 0;
5721
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005722 // take care of cached line first
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005723 ml_flush_line(curbuf);
5724
Bram Moolenaar071d4272004-06-13 20:20:40 +00005725 if (buf->b_ml.ml_usedchunks == -1
5726 || buf->b_ml.ml_chunksize == NULL
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005727 || lnum < 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005728 return -1;
5729
5730 if (offp == NULL)
5731 offset = 0;
5732 else
5733 offset = *offp;
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005734 if (lnum == 0 && offset <= 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005735 return 1; // Not a "find offset" and offset 0 _must_ be in line 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00005736 /*
5737 * Find the last chunk before the one containing our line. Last chunk is
5738 * special because it will never qualify
5739 */
5740 curline = 1;
5741 curix = size = 0;
5742 while (curix < buf->b_ml.ml_usedchunks - 1
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005743 && ((lnum != 0
5744 && lnum >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005745 || (offset != 0
5746 && offset > size + buf->b_ml.ml_chunksize[curix].mlcs_totalsize
5747 + ffdos * buf->b_ml.ml_chunksize[curix].mlcs_numlines)))
5748 {
5749 curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
5750 size += buf->b_ml.ml_chunksize[curix].mlcs_totalsize;
5751 if (offset && ffdos)
5752 size += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
5753 curix++;
5754 }
5755
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005756 while ((lnum != 0 && curline < lnum) || (offset != 0 && size < offset))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005757 {
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005758#ifdef FEAT_PROP_POPUP
5759 size_t textprop_total = 0;
5760#endif
5761
Bram Moolenaar071d4272004-06-13 20:20:40 +00005762 if (curline > buf->b_ml.ml_line_count
5763 || (hp = ml_find_line(buf, curline, ML_FIND)) == NULL)
5764 return -1;
5765 dp = (DATA_BL *)(hp->bh_data);
5766 count = (long)(buf->b_ml.ml_locked_high) -
5767 (long)(buf->b_ml.ml_locked_low) + 1;
5768 start_idx = idx = curline - buf->b_ml.ml_locked_low;
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005769 if (idx == 0) // first line in block, text at the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00005770 text_end = dp->db_txt_end;
5771 else
5772 text_end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005773 // Compute index of last line to use in this MEMLINE
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005774 if (lnum != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005775 {
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005776 if (curline + (count - idx) >= lnum)
5777 idx += lnum - curline - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005778 else
5779 idx = count - 1;
5780 }
5781 else
5782 {
5783 extra = 0;
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005784 for (;;)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005785 {
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005786#ifdef FEAT_PROP_POPUP
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005787 size_t textprop_size = 0;
5788
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005789 if (buf->b_has_textprop)
5790 {
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005791 char_u *l1, *l2;
5792
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005793 // compensate for the extra bytes taken by textprops
5794 l1 = (char_u *)dp + ((dp->db_index[idx]) & DB_INDEX_MASK);
5795 l2 = (char_u *)dp + (idx == 0 ? dp->db_txt_end
5796 : ((dp->db_index[idx - 1]) & DB_INDEX_MASK));
5797 textprop_size = (l2 - l1) - (STRLEN(l1) + 1);
5798 }
5799#endif
5800 if (!(offset >= size
5801 + text_end - (int)((dp->db_index[idx]) & DB_INDEX_MASK)
5802#ifdef FEAT_PROP_POPUP
Bram Moolenaar94b6fb72020-01-17 21:00:59 +01005803 - (long)(textprop_total + textprop_size)
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005804#endif
5805 + ffdos))
5806 break;
5807
Bram Moolenaar071d4272004-06-13 20:20:40 +00005808 if (ffdos)
5809 size++;
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005810#ifdef FEAT_PROP_POPUP
5811 textprop_total += textprop_size;
5812#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005813 if (idx == count - 1)
5814 {
5815 extra = 1;
5816 break;
5817 }
5818 idx++;
5819 }
5820 }
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01005821#ifdef FEAT_PROP_POPUP
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005822 if (buf->b_has_textprop && lnum != 0)
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005823 {
5824 int i;
5825
5826 // cannot use the db_index pointer, need to get the actual text
5827 // lengths.
5828 len = 0;
5829 for (i = start_idx; i <= idx; ++i)
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005830 {
5831 char_u *p = (char_u *)dp + ((dp->db_index[i]) & DB_INDEX_MASK);
5832 len += (int)STRLEN(p) + 1;
5833 }
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005834 }
5835 else
5836#endif
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005837 len = text_end - ((dp->db_index[idx]) & DB_INDEX_MASK)
5838#ifdef FEAT_PROP_POPUP
5839 - (long)textprop_total
5840#endif
5841 ;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005842 size += len;
5843 if (offset != 0 && size >= offset)
5844 {
5845 if (size + ffdos == offset)
5846 *offp = 0;
5847 else if (idx == start_idx)
5848 *offp = offset - size + len;
5849 else
5850 *offp = offset - size + len
Bram Moolenaar59ff6402021-01-30 17:16:28 +01005851 - (text_end - ((dp->db_index[idx - 1]) & DB_INDEX_MASK))
5852#ifdef FEAT_PROP_POPUP
5853 + (long)textprop_total
5854#endif
5855 ;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005856 curline += idx - start_idx + extra;
5857 if (curline > buf->b_ml.ml_line_count)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005858 return -1; // exactly one byte beyond the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00005859 return curline;
5860 }
5861 curline = buf->b_ml.ml_locked_high + 1;
5862 }
5863
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005864 if (lnum != 0)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005865 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005866 // Count extra CR characters.
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005867 if (ffdos)
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005868 size += lnum - 1;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005869
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005870 // Don't count the last line break if 'noeol' and ('bin' or
5871 // 'nofixeol').
Bram Moolenaar34d72d42015-07-17 14:18:08 +02005872 if ((!buf->b_p_fixeol || buf->b_p_bin) && !buf->b_p_eol
Bram Moolenaarc26f7c62018-08-20 22:53:04 +02005873 && lnum > buf->b_ml.ml_line_count)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005874 size -= ffdos + 1;
5875 }
5876
Bram Moolenaar071d4272004-06-13 20:20:40 +00005877 return size;
5878}
5879
5880/*
5881 * Goto byte in buffer with offset 'cnt'.
5882 */
5883 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005884goto_byte(long cnt)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005885{
5886 long boff = cnt;
5887 linenr_T lnum;
5888
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005889 ml_flush_line(curbuf); // cached line may be dirty
Bram Moolenaar071d4272004-06-13 20:20:40 +00005890 setpcmark();
5891 if (boff)
5892 --boff;
5893 lnum = ml_find_line_or_offset(curbuf, (linenr_T)0, &boff);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005894 if (lnum < 1) // past the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00005895 {
5896 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
5897 curwin->w_curswant = MAXCOL;
5898 coladvance((colnr_T)MAXCOL);
5899 }
5900 else
5901 {
5902 curwin->w_cursor.lnum = lnum;
5903 curwin->w_cursor.col = (colnr_T)boff;
Bram Moolenaar943d2b52005-12-02 00:50:49 +00005904 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005905 curwin->w_set_curswant = TRUE;
5906 }
5907 check_cursor();
5908
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005909 // Make sure the cursor is on the first byte of a multi-byte char.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005910 if (has_mbyte)
5911 mb_adjust_cursor();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005912}
5913#endif