blob: 084a8b0fa4e573c8d4e76cf58dd57575fa13954d [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010010// for debugging
11// #define CHECK(c, s) do { if (c) emsg((s)); } while (0)
Bram Moolenaar6f470022018-04-10 18:47:20 +020012#define CHECK(c, s) do { /**/ } while (0)
Bram Moolenaar071d4272004-06-13 20:20:40 +000013
14/*
15 * memline.c: Contains the functions for appending, deleting and changing the
Bram Moolenaar4770d092006-01-12 23:22:24 +000016 * text lines. The memfile functions are used to store the information in
17 * blocks of memory, backed up by a file. The structure of the information is
18 * a tree. The root of the tree is a pointer block. The leaves of the tree
19 * are data blocks. In between may be several layers of pointer blocks,
20 * forming branches.
Bram Moolenaar071d4272004-06-13 20:20:40 +000021 *
22 * Three types of blocks are used:
23 * - Block nr 0 contains information for recovery
24 * - Pointer blocks contain list of pointers to other blocks.
25 * - Data blocks contain the actual text.
26 *
27 * Block nr 0 contains the block0 structure (see below).
28 *
29 * Block nr 1 is the first pointer block. It is the root of the tree.
30 * Other pointer blocks are branches.
31 *
32 * If a line is too big to fit in a single page, the block containing that
33 * line is made big enough to hold the line. It may span several pages.
34 * Otherwise all blocks are one page.
35 *
36 * A data block that was filled when starting to edit a file and was not
37 * changed since then, can have a negative block number. This means that it
38 * has not yet been assigned a place in the file. When recovering, the lines
39 * in this data block can be read from the original file. When the block is
40 * changed (lines appended/deleted/changed) or when it is flushed it gets a
41 * positive number. Use mf_trans_del() to get the new number, before calling
42 * mf_get().
43 */
44
Bram Moolenaar071d4272004-06-13 20:20:40 +000045#include "vim.h"
46
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010047#ifndef UNIX // it's in os_unix.h for Unix
Bram Moolenaar071d4272004-06-13 20:20:40 +000048# include <time.h>
49#endif
50
Bram Moolenaar5a6404c2006-11-01 17:12:57 +000051#if defined(SASC) || defined(__amigaos4__)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010052# include <proto/dos.h> // for Open() and Close()
Bram Moolenaar071d4272004-06-13 20:20:40 +000053#endif
54
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010055typedef struct block0 ZERO_BL; // contents of the first block
56typedef struct pointer_block PTR_BL; // contents of a pointer block
57typedef struct data_block DATA_BL; // contents of a data block
58typedef struct pointer_entry PTR_EN; // block/line-count pair
Bram Moolenaar071d4272004-06-13 20:20:40 +000059
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010060#define DATA_ID (('d' << 8) + 'a') // data block id
61#define PTR_ID (('p' << 8) + 't') // pointer block id
62#define BLOCK0_ID0 'b' // block 0 id 0
63#define BLOCK0_ID1 '0' // block 0 id 1
64#define BLOCK0_ID1_C0 'c' // block 0 id 1 'cm' 0
65#define BLOCK0_ID1_C1 'C' // block 0 id 1 'cm' 1
66#define BLOCK0_ID1_C2 'd' // block 0 id 1 'cm' 2
Bram Moolenaar8f4ac012014-08-10 13:38:34 +020067
68#if defined(FEAT_CRYPT)
69static int id1_codes[] = {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010070 BLOCK0_ID1_C0, // CRYPT_M_ZIP
71 BLOCK0_ID1_C1, // CRYPT_M_BF
72 BLOCK0_ID1_C2, // CRYPT_M_BF2
Bram Moolenaar8f4ac012014-08-10 13:38:34 +020073};
74#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000075
76/*
77 * pointer to a block, used in a pointer block
78 */
79struct pointer_entry
80{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010081 blocknr_T pe_bnum; // block number
82 linenr_T pe_line_count; // number of lines in this branch
83 linenr_T pe_old_lnum; // lnum for this block (for recovery)
84 int pe_page_count; // number of pages in block pe_bnum
Bram Moolenaar071d4272004-06-13 20:20:40 +000085};
86
87/*
88 * A pointer block contains a list of branches in the tree.
89 */
90struct pointer_block
91{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010092 short_u pb_id; // ID for pointer block: PTR_ID
93 short_u pb_count; // number of pointers in this block
94 short_u pb_count_max; // maximum value for pb_count
95 PTR_EN pb_pointer[1]; // list of pointers to blocks (actually longer)
96 // followed by empty space until end of page
Bram Moolenaar071d4272004-06-13 20:20:40 +000097};
98
99/*
100 * A data block is a leaf in the tree.
101 *
102 * The text of the lines is at the end of the block. The text of the first line
103 * in the block is put at the end, the text of the second line in front of it,
104 * etc. Thus the order of the lines is the opposite of the line number.
105 */
106struct data_block
107{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100108 short_u db_id; // ID for data block: DATA_ID
109 unsigned db_free; // free space available
110 unsigned db_txt_start; // byte where text starts
111 unsigned db_txt_end; // byte just after data block
112 linenr_T db_line_count; // number of lines in this block
113 unsigned db_index[1]; // index for start of line (actually bigger)
Bram Moolenaar4b96df52020-01-26 22:00:26 +0100114 // followed by empty space up to db_txt_start
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100115 // followed by the text in the lines until
116 // end of page
Bram Moolenaar071d4272004-06-13 20:20:40 +0000117};
118
119/*
120 * The low bits of db_index hold the actual index. The topmost bit is
121 * used for the global command to be able to mark a line.
122 * This method is not clean, but otherwise there would be at least one extra
123 * byte used for each line.
124 * The mark has to be in this place to keep it with the correct line when other
125 * lines are inserted or deleted.
126 */
127#define DB_MARKED ((unsigned)1 << ((sizeof(unsigned) * 8) - 1))
128#define DB_INDEX_MASK (~DB_MARKED)
129
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100130#define INDEX_SIZE (sizeof(unsigned)) // size of one db_index entry
131#define HEADER_SIZE (sizeof(DATA_BL) - INDEX_SIZE) // size of data block header
Bram Moolenaar071d4272004-06-13 20:20:40 +0000132
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100133#define B0_FNAME_SIZE_ORG 900 // what it was in older versions
134#define B0_FNAME_SIZE_NOCRYPT 898 // 2 bytes used for other things
135#define B0_FNAME_SIZE_CRYPT 890 // 10 bytes used for other things
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000136#define B0_UNAME_SIZE 40
137#define B0_HNAME_SIZE 40
Bram Moolenaar071d4272004-06-13 20:20:40 +0000138/*
139 * Restrict the numbers to 32 bits, otherwise most compilers will complain.
140 * This won't detect a 64 bit machine that only swaps a byte in the top 32
141 * bits, but that is crazy anyway.
142 */
143#define B0_MAGIC_LONG 0x30313233L
144#define B0_MAGIC_INT 0x20212223L
145#define B0_MAGIC_SHORT 0x10111213L
146#define B0_MAGIC_CHAR 0x55
147
148/*
149 * Block zero holds all info about the swap file.
150 *
151 * NOTE: DEFINITION OF BLOCK 0 SHOULD NOT CHANGE! It would make all existing
152 * swap files unusable!
153 *
154 * If size of block0 changes anyway, adjust MIN_SWAP_PAGE_SIZE in vim.h!!
155 *
Bram Moolenaarbae0c162007-05-10 19:30:25 +0000156 * This block is built up of single bytes, to make it portable across
Bram Moolenaar071d4272004-06-13 20:20:40 +0000157 * different machines. b0_magic_* is used to check the byte order and size of
158 * variables, because the rest of the swap file is not portable.
159 */
160struct block0
161{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100162 char_u b0_id[2]; // id for block 0: BLOCK0_ID0 and BLOCK0_ID1,
163 // BLOCK0_ID1_C0, BLOCK0_ID1_C1, etc.
164 char_u b0_version[10]; // Vim version string
165 char_u b0_page_size[4];// number of bytes per page
166 char_u b0_mtime[4]; // last modification time of file
167 char_u b0_ino[4]; // inode of b0_fname
168 char_u b0_pid[4]; // process id of creator (or 0)
169 char_u b0_uname[B0_UNAME_SIZE]; // name of user (uid if no name)
170 char_u b0_hname[B0_HNAME_SIZE]; // host name (if it has a name)
171 char_u b0_fname[B0_FNAME_SIZE_ORG]; // name of file being edited
172 long b0_magic_long; // check for byte order of long
173 int b0_magic_int; // check for byte order of int
174 short b0_magic_short; // check for byte order of short
175 char_u b0_magic_char; // check for last char
Bram Moolenaar071d4272004-06-13 20:20:40 +0000176};
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000177
178/*
Bram Moolenaar4770d092006-01-12 23:22:24 +0000179 * Note: b0_dirty and b0_flags are put at the end of the file name. For very
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000180 * long file names in older versions of Vim they are invalid.
181 * The 'fileencoding' comes before b0_flags, with a NUL in front. But only
182 * when there is room, for very long file names it's omitted.
183 */
184#define B0_DIRTY 0x55
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200185#define b0_dirty b0_fname[B0_FNAME_SIZE_ORG - 1]
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000186
187/*
188 * The b0_flags field is new in Vim 7.0.
189 */
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200190#define b0_flags b0_fname[B0_FNAME_SIZE_ORG - 2]
191
192/*
193 * Crypt seed goes here, 8 bytes. New in Vim 7.3.
194 * Without encryption these bytes may be used for 'fenc'.
195 */
196#define b0_seed b0_fname[B0_FNAME_SIZE_ORG - 2 - MF_SEED_LEN]
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000197
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100198// The lowest two bits contain the fileformat. Zero means it's not set
199// (compatible with Vim 6.x), otherwise it's EOL_UNIX + 1, EOL_DOS + 1 or
200// EOL_MAC + 1.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000201#define B0_FF_MASK 3
202
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100203// Swap file is in directory of edited file. Used to find the file from
204// different mount points.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000205#define B0_SAME_DIR 4
206
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100207// The 'fileencoding' is at the end of b0_fname[], with a NUL in front of it.
208// When empty there is only the NUL.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000209#define B0_HAS_FENC 8
Bram Moolenaar071d4272004-06-13 20:20:40 +0000210
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100211#define STACK_INCR 5 // nr of entries added to ml_stack at a time
Bram Moolenaar071d4272004-06-13 20:20:40 +0000212
213/*
214 * The line number where the first mark may be is remembered.
215 * If it is 0 there are no marks at all.
216 * (always used for the current buffer only, no buffer change possible while
217 * executing a global command).
218 */
219static linenr_T lowest_marked = 0;
220
221/*
222 * arguments for ml_find_line()
223 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100224#define ML_DELETE 0x11 // delete line
225#define ML_INSERT 0x12 // insert line
226#define ML_FIND 0x13 // just find the line
227#define ML_FLUSH 0x02 // flush locked block
228#define ML_SIMPLE(x) (x & 0x10) // DEL, INS or FIND
Bram Moolenaar071d4272004-06-13 20:20:40 +0000229
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100230// argument for ml_upd_block0()
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200231typedef enum {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100232 UB_FNAME = 0 // update timestamp and filename
233 , UB_SAME_DIR // update the B0_SAME_DIR flag
234 , UB_CRYPT // update crypt key
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200235} upd_block0_T;
236
237#ifdef FEAT_CRYPT
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100238static void ml_set_b0_crypt(buf_T *buf, ZERO_BL *b0p);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200239#endif
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100240static void ml_upd_block0(buf_T *buf, upd_block0_T what);
241static void set_b0_fname(ZERO_BL *, buf_T *buf);
242static void set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100243static void add_b0_fenc(ZERO_BL *b0p, buf_T *buf);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100244static time_t swapfile_info(char_u *);
245static int recov_file_names(char_u **, char_u *, int prepend_dot);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100246static char_u *findswapname(buf_T *, char_u **, char_u *);
247static void ml_flush_line(buf_T *);
248static bhdr_T *ml_new_data(memfile_T *, int, int);
249static bhdr_T *ml_new_ptr(memfile_T *);
250static bhdr_T *ml_find_line(buf_T *, linenr_T, int);
251static int ml_add_stack(buf_T *);
252static void ml_lineadd(buf_T *, int);
253static int b0_magic_wrong(ZERO_BL *);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000254#ifdef CHECK_INODE
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100255static int fnamecmp_ino(char_u *, char_u *, long);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000256#endif
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100257static void long_to_char(long, char_u *);
258static long char_to_long(char_u *);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200259#ifdef FEAT_CRYPT
Bram Moolenaar8767f522016-07-01 17:17:39 +0200260static cryptstate_T *ml_crypt_prepare(memfile_T *mfp, off_T offset, int reading);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200261#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000262#ifdef FEAT_BYTEOFF
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100263static void ml_updatechunk(buf_T *buf, long line, long len, int updtype);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000264#endif
265
266/*
Bram Moolenaar4770d092006-01-12 23:22:24 +0000267 * Open a new memline for "buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +0000268 *
Bram Moolenaar4770d092006-01-12 23:22:24 +0000269 * Return FAIL for failure, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000270 */
271 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100272ml_open(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000273{
274 memfile_T *mfp;
275 bhdr_T *hp = NULL;
276 ZERO_BL *b0p;
277 PTR_BL *pp;
278 DATA_BL *dp;
279
Bram Moolenaar4770d092006-01-12 23:22:24 +0000280 /*
281 * init fields in memline struct
282 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100283 buf->b_ml.ml_stack_size = 0; // no stack yet
284 buf->b_ml.ml_stack = NULL; // no stack yet
285 buf->b_ml.ml_stack_top = 0; // nothing in the stack
286 buf->b_ml.ml_locked = NULL; // no cached block
287 buf->b_ml.ml_line_lnum = 0; // no cached line
Bram Moolenaar071d4272004-06-13 20:20:40 +0000288#ifdef FEAT_BYTEOFF
Bram Moolenaar4770d092006-01-12 23:22:24 +0000289 buf->b_ml.ml_chunksize = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000290#endif
291
Bram Moolenaar5803ae62014-03-23 16:04:02 +0100292 if (cmdmod.noswapfile)
293 buf->b_p_swf = FALSE;
294
Bram Moolenaar4770d092006-01-12 23:22:24 +0000295 /*
296 * When 'updatecount' is non-zero swap file may be opened later.
297 */
298 if (p_uc && buf->b_p_swf)
299 buf->b_may_swap = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000300 else
Bram Moolenaar4770d092006-01-12 23:22:24 +0000301 buf->b_may_swap = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000302
Bram Moolenaar4770d092006-01-12 23:22:24 +0000303 /*
304 * Open the memfile. No swap file is created yet.
305 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000306 mfp = mf_open(NULL, 0);
307 if (mfp == NULL)
308 goto error;
309
Bram Moolenaar4770d092006-01-12 23:22:24 +0000310 buf->b_ml.ml_mfp = mfp;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200311#ifdef FEAT_CRYPT
312 mfp->mf_buffer = buf;
313#endif
Bram Moolenaar4770d092006-01-12 23:22:24 +0000314 buf->b_ml.ml_flags = ML_EMPTY;
315 buf->b_ml.ml_line_count = 1;
Bram Moolenaar592e0a22004-07-03 16:05:59 +0000316#ifdef FEAT_LINEBREAK
317 curwin->w_nrwidth_line_count = 0;
318#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000319
Bram Moolenaar071d4272004-06-13 20:20:40 +0000320/*
321 * fill block0 struct and write page 0
322 */
323 if ((hp = mf_new(mfp, FALSE, 1)) == NULL)
324 goto error;
325 if (hp->bh_bnum != 0)
326 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100327 iemsg(_("E298: Didn't get block nr 0?"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000328 goto error;
329 }
330 b0p = (ZERO_BL *)(hp->bh_data);
331
332 b0p->b0_id[0] = BLOCK0_ID0;
333 b0p->b0_id[1] = BLOCK0_ID1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000334 b0p->b0_magic_long = (long)B0_MAGIC_LONG;
335 b0p->b0_magic_int = (int)B0_MAGIC_INT;
336 b0p->b0_magic_short = (short)B0_MAGIC_SHORT;
337 b0p->b0_magic_char = B0_MAGIC_CHAR;
Bram Moolenaar22c10562018-05-26 17:35:27 +0200338 mch_memmove(b0p->b0_version, "VIM ", 4);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000339 STRNCPY(b0p->b0_version + 4, Version, 6);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000340 long_to_char((long)mfp->mf_page_size, b0p->b0_page_size);
Bram Moolenaar4770d092006-01-12 23:22:24 +0000341
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000342#ifdef FEAT_SPELL
343 if (!buf->b_spell)
344#endif
Bram Moolenaar4770d092006-01-12 23:22:24 +0000345 {
346 b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0;
347 b0p->b0_flags = get_fileformat(buf) + 1;
348 set_b0_fname(b0p, buf);
349 (void)get_user_name(b0p->b0_uname, B0_UNAME_SIZE);
350 b0p->b0_uname[B0_UNAME_SIZE - 1] = NUL;
351 mch_get_host_name(b0p->b0_hname, B0_HNAME_SIZE);
352 b0p->b0_hname[B0_HNAME_SIZE - 1] = NUL;
353 long_to_char(mch_get_pid(), b0p->b0_pid);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200354#ifdef FEAT_CRYPT
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200355 ml_set_b0_crypt(buf, b0p);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200356#endif
Bram Moolenaar4770d092006-01-12 23:22:24 +0000357 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000358
359 /*
360 * Always sync block number 0 to disk, so we can check the file name in
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200361 * the swap file in findswapname(). Don't do this for a help files or
362 * a spell buffer though.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000363 * Only works when there's a swapfile, otherwise it's done when the file
364 * is created.
365 */
366 mf_put(mfp, hp, TRUE, FALSE);
Bram Moolenaar4770d092006-01-12 23:22:24 +0000367 if (!buf->b_help && !B_SPELL(buf))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000368 (void)mf_sync(mfp, 0);
369
Bram Moolenaar4770d092006-01-12 23:22:24 +0000370 /*
371 * Fill in root pointer block and write page 1.
372 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000373 if ((hp = ml_new_ptr(mfp)) == NULL)
374 goto error;
375 if (hp->bh_bnum != 1)
376 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100377 iemsg(_("E298: Didn't get block nr 1?"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000378 goto error;
379 }
380 pp = (PTR_BL *)(hp->bh_data);
381 pp->pb_count = 1;
382 pp->pb_pointer[0].pe_bnum = 2;
383 pp->pb_pointer[0].pe_page_count = 1;
384 pp->pb_pointer[0].pe_old_lnum = 1;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100385 pp->pb_pointer[0].pe_line_count = 1; // line count after insertion
Bram Moolenaar071d4272004-06-13 20:20:40 +0000386 mf_put(mfp, hp, TRUE, FALSE);
387
Bram Moolenaar4770d092006-01-12 23:22:24 +0000388 /*
389 * Allocate first data block and create an empty line 1.
390 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000391 if ((hp = ml_new_data(mfp, FALSE, 1)) == NULL)
392 goto error;
393 if (hp->bh_bnum != 2)
394 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100395 iemsg(_("E298: Didn't get block nr 2?"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000396 goto error;
397 }
398
399 dp = (DATA_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100400 dp->db_index[0] = --dp->db_txt_start; // at end of block
Bram Moolenaar071d4272004-06-13 20:20:40 +0000401 dp->db_free -= 1 + INDEX_SIZE;
402 dp->db_line_count = 1;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100403 *((char_u *)dp + dp->db_txt_start) = NUL; // empty line
Bram Moolenaar071d4272004-06-13 20:20:40 +0000404
405 return OK;
406
407error:
408 if (mfp != NULL)
409 {
410 if (hp)
411 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100412 mf_close(mfp, TRUE); // will also free(mfp->mf_fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000413 }
Bram Moolenaar4770d092006-01-12 23:22:24 +0000414 buf->b_ml.ml_mfp = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000415 return FAIL;
416}
417
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200418#if defined(FEAT_CRYPT) || defined(PROTO)
419/*
Bram Moolenaar2be79502014-08-13 21:58:28 +0200420 * Prepare encryption for "buf" for the current key and method.
421 */
422 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100423ml_set_mfp_crypt(buf_T *buf)
Bram Moolenaar2be79502014-08-13 21:58:28 +0200424{
425 if (*buf->b_p_key != NUL)
426 {
427 int method_nr = crypt_get_method_nr(buf);
428
429 if (method_nr > CRYPT_M_ZIP)
430 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100431 // Generate a seed and store it in the memfile.
Bram Moolenaar2be79502014-08-13 21:58:28 +0200432 sha2_seed(buf->b_ml.ml_mfp->mf_seed, MF_SEED_LEN, NULL, 0);
433 }
434 }
435}
436
437/*
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200438 * Prepare encryption for "buf" with block 0 "b0p".
439 */
440 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100441ml_set_b0_crypt(buf_T *buf, ZERO_BL *b0p)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200442{
443 if (*buf->b_p_key == NUL)
444 b0p->b0_id[1] = BLOCK0_ID1;
445 else
446 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200447 int method_nr = crypt_get_method_nr(buf);
448
449 b0p->b0_id[1] = id1_codes[method_nr];
450 if (method_nr > CRYPT_M_ZIP)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200451 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100452 // Generate a seed and store it in block 0 and in the memfile.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200453 sha2_seed(&b0p->b0_seed, MF_SEED_LEN, NULL, 0);
454 mch_memmove(buf->b_ml.ml_mfp->mf_seed, &b0p->b0_seed, MF_SEED_LEN);
455 }
456 }
457}
458
459/*
460 * Called after the crypt key or 'cryptmethod' was changed for "buf".
461 * Will apply this to the swapfile.
462 * "old_key" is the previous key. It is equal to buf->b_p_key when
463 * 'cryptmethod' is changed.
Bram Moolenaar49771f42010-07-20 17:32:38 +0200464 * "old_cm" is the previous 'cryptmethod'. It is equal to the current
465 * 'cryptmethod' when 'key' is changed.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200466 */
467 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100468ml_set_crypt_key(
469 buf_T *buf,
470 char_u *old_key,
471 char_u *old_cm)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200472{
473 memfile_T *mfp = buf->b_ml.ml_mfp;
474 bhdr_T *hp;
475 int page_count;
476 int idx;
477 long error;
478 infoptr_T *ip;
479 PTR_BL *pp;
480 DATA_BL *dp;
481 blocknr_T bnum;
482 int top;
Bram Moolenaarbc563362015-06-09 18:35:25 +0200483 int old_method;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200484
Bram Moolenaar3832c462010-08-04 15:32:46 +0200485 if (mfp == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100486 return; // no memfile yet, nothing to do
Bram Moolenaarbc563362015-06-09 18:35:25 +0200487 old_method = crypt_method_nr_from_name(old_cm);
488
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100489 // First make sure the swapfile is in a consistent state, using the old
490 // key and method.
Bram Moolenaarbc563362015-06-09 18:35:25 +0200491 {
492 char_u *new_key = buf->b_p_key;
493 char_u *new_buf_cm = buf->b_p_cm;
494
495 buf->b_p_key = old_key;
496 buf->b_p_cm = old_cm;
497 ml_preserve(buf, FALSE);
498 buf->b_p_key = new_key;
499 buf->b_p_cm = new_buf_cm;
500 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200501
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100502 // Set the key, method and seed to be used for reading, these must be the
503 // old values.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200504 mfp->mf_old_key = old_key;
Bram Moolenaarbc563362015-06-09 18:35:25 +0200505 mfp->mf_old_cm = old_method;
506 if (old_method > 0 && *old_key != NUL)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200507 mch_memmove(mfp->mf_old_seed, mfp->mf_seed, MF_SEED_LEN);
508
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100509 // Update block 0 with the crypt flag and may set a new seed.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200510 ml_upd_block0(buf, UB_CRYPT);
511
512 if (mfp->mf_infile_count > 2)
513 {
514 /*
515 * Need to read back all data blocks from disk, decrypt them with the
516 * old key/method and mark them to be written. The algorithm is
517 * similar to what happens in ml_recover(), but we skip negative block
518 * numbers.
519 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100520 ml_flush_line(buf); // flush buffered line
521 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush locked block
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200522
523 hp = NULL;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100524 bnum = 1; // start with block 1
525 page_count = 1; // which is 1 page
526 idx = 0; // start with first index in block 1
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200527 error = 0;
528 buf->b_ml.ml_stack_top = 0;
Bram Moolenaard23a8232018-02-10 18:45:26 +0100529 VIM_CLEAR(buf->b_ml.ml_stack);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100530 buf->b_ml.ml_stack_size = 0; // no stack yet
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200531
532 for ( ; !got_int; line_breakcheck())
533 {
534 if (hp != NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100535 mf_put(mfp, hp, FALSE, FALSE); // release previous block
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200536
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100537 // get the block (pointer or data)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200538 if ((hp = mf_get(mfp, (blocknr_T)bnum, page_count)) == NULL)
539 {
540 if (bnum == 1)
541 break;
542 ++error;
543 }
544 else
545 {
546 pp = (PTR_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100547 if (pp->pb_id == PTR_ID) // it is a pointer block
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200548 {
549 if (pp->pb_count == 0)
550 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100551 // empty block?
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200552 ++error;
553 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100554 else if (idx < (int)pp->pb_count) // go a block deeper
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200555 {
556 if (pp->pb_pointer[idx].pe_bnum < 0)
557 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100558 // Skip data block with negative block number.
559 // Should not happen, because of the ml_preserve()
560 // above. Get same block again for next index.
Bram Moolenaar4b7214e2019-01-03 21:55:32 +0100561 ++idx;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200562 continue;
563 }
564
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100565 // going one block deeper in the tree, new entry in
566 // stack
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200567 if ((top = ml_add_stack(buf)) < 0)
568 {
569 ++error;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100570 break; // out of memory
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200571 }
572 ip = &(buf->b_ml.ml_stack[top]);
573 ip->ip_bnum = bnum;
574 ip->ip_index = idx;
575
576 bnum = pp->pb_pointer[idx].pe_bnum;
577 page_count = pp->pb_pointer[idx].pe_page_count;
Bram Moolenaarbc563362015-06-09 18:35:25 +0200578 idx = 0;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200579 continue;
580 }
581 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100582 else // not a pointer block
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200583 {
584 dp = (DATA_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100585 if (dp->db_id != DATA_ID) // block id wrong
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200586 ++error;
587 else
588 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100589 // It is a data block, need to write it back to disk.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200590 mf_put(mfp, hp, TRUE, FALSE);
591 hp = NULL;
592 }
593 }
594 }
595
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100596 if (buf->b_ml.ml_stack_top == 0) // finished
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200597 break;
598
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100599 // go one block up in the tree
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200600 ip = &(buf->b_ml.ml_stack[--(buf->b_ml.ml_stack_top)]);
601 bnum = ip->ip_bnum;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100602 idx = ip->ip_index + 1; // go to next index
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200603 page_count = 1;
604 }
Bram Moolenaarbc563362015-06-09 18:35:25 +0200605 if (hp != NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100606 mf_put(mfp, hp, FALSE, FALSE); // release previous block
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +0100607
608 if (error > 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100609 emsg(_("E843: Error while updating swap file crypt"));
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200610 }
611
612 mfp->mf_old_key = NULL;
613}
614#endif
615
Bram Moolenaar071d4272004-06-13 20:20:40 +0000616/*
617 * ml_setname() is called when the file name of "buf" has been changed.
618 * It may rename the swap file.
619 */
620 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100621ml_setname(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000622{
623 int success = FALSE;
624 memfile_T *mfp;
625 char_u *fname;
626 char_u *dirp;
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100627#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000628 char_u *p;
629#endif
630
631 mfp = buf->b_ml.ml_mfp;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100632 if (mfp->mf_fd < 0) // there is no swap file yet
Bram Moolenaar071d4272004-06-13 20:20:40 +0000633 {
634 /*
635 * When 'updatecount' is 0 and 'noswapfile' there is no swap file.
636 * For help files we will make a swap file now.
637 */
Bram Moolenaar5803ae62014-03-23 16:04:02 +0100638 if (p_uc != 0 && !cmdmod.noswapfile)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100639 ml_open_file(buf); // create a swap file
Bram Moolenaar071d4272004-06-13 20:20:40 +0000640 return;
641 }
642
643 /*
644 * Try all directories in the 'directory' option.
645 */
646 dirp = p_dir;
647 for (;;)
648 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100649 if (*dirp == NUL) // tried all directories, fail
Bram Moolenaar071d4272004-06-13 20:20:40 +0000650 break;
Bram Moolenaar8fc061c2004-12-29 21:03:02 +0000651 fname = findswapname(buf, &dirp, mfp->mf_fname);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100652 // alloc's fname
653 if (dirp == NULL) // out of memory
Bram Moolenaarf541c362011-10-26 11:44:18 +0200654 break;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100655 if (fname == NULL) // no file name found for this dir
Bram Moolenaar071d4272004-06-13 20:20:40 +0000656 continue;
657
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100658#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000659 /*
660 * Set full pathname for swap file now, because a ":!cd dir" may
661 * change directory without us knowing it.
662 */
663 p = FullName_save(fname, FALSE);
664 vim_free(fname);
665 fname = p;
666 if (fname == NULL)
667 continue;
668#endif
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100669 // if the file name is the same we don't have to do anything
Bram Moolenaar071d4272004-06-13 20:20:40 +0000670 if (fnamecmp(fname, mfp->mf_fname) == 0)
671 {
672 vim_free(fname);
673 success = TRUE;
674 break;
675 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100676 // need to close the swap file before renaming
Bram Moolenaar071d4272004-06-13 20:20:40 +0000677 if (mfp->mf_fd >= 0)
678 {
679 close(mfp->mf_fd);
680 mfp->mf_fd = -1;
681 }
682
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100683 // try to rename the swap file
Bram Moolenaar071d4272004-06-13 20:20:40 +0000684 if (vim_rename(mfp->mf_fname, fname) == 0)
685 {
686 success = TRUE;
687 vim_free(mfp->mf_fname);
688 mfp->mf_fname = fname;
689 vim_free(mfp->mf_ffname);
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100690#if defined(MSWIN)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100691 mfp->mf_ffname = NULL; // mf_fname is full pathname already
Bram Moolenaar071d4272004-06-13 20:20:40 +0000692#else
693 mf_set_ffname(mfp);
694#endif
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200695 ml_upd_block0(buf, UB_SAME_DIR);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000696 break;
697 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100698 vim_free(fname); // this fname didn't work, try another
Bram Moolenaar071d4272004-06-13 20:20:40 +0000699 }
700
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100701 if (mfp->mf_fd == -1) // need to (re)open the swap file
Bram Moolenaar071d4272004-06-13 20:20:40 +0000702 {
703 mfp->mf_fd = mch_open((char *)mfp->mf_fname, O_RDWR | O_EXTRA, 0);
704 if (mfp->mf_fd < 0)
705 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100706 // could not (re)open the swap file, what can we do????
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100707 emsg(_("E301: Oops, lost the swap file!!!"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000708 return;
709 }
Bram Moolenaarf05da212009-11-17 16:13:15 +0000710#ifdef HAVE_FD_CLOEXEC
711 {
712 int fdflags = fcntl(mfp->mf_fd, F_GETFD);
713 if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0)
Bram Moolenaarfbc4b4d2016-02-07 15:14:01 +0100714 (void)fcntl(mfp->mf_fd, F_SETFD, fdflags | FD_CLOEXEC);
Bram Moolenaarf05da212009-11-17 16:13:15 +0000715 }
716#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000717 }
718 if (!success)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100719 emsg(_("E302: Could not rename swap file"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000720}
721
722/*
723 * Open a file for the memfile for all buffers that are not readonly or have
724 * been modified.
725 * Used when 'updatecount' changes from zero to non-zero.
726 */
727 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100728ml_open_files(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000729{
730 buf_T *buf;
731
Bram Moolenaar29323592016-07-24 22:04:11 +0200732 FOR_ALL_BUFFERS(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000733 if (!buf->b_p_ro || buf->b_changed)
734 ml_open_file(buf);
735}
736
737/*
738 * Open a swap file for an existing memfile, if there is no swap file yet.
739 * If we are unable to find a file name, mf_fname will be NULL
740 * and the memfile will be in memory only (no recovery possible).
741 */
742 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100743ml_open_file(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000744{
745 memfile_T *mfp;
746 char_u *fname;
747 char_u *dirp;
748
749 mfp = buf->b_ml.ml_mfp;
Bram Moolenaar5803ae62014-03-23 16:04:02 +0100750 if (mfp == NULL || mfp->mf_fd >= 0 || !buf->b_p_swf || cmdmod.noswapfile)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100751 return; // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +0000752
Bram Moolenaara1956f62006-03-12 22:18:00 +0000753#ifdef FEAT_SPELL
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100754 // For a spell buffer use a temp file name.
Bram Moolenaar4770d092006-01-12 23:22:24 +0000755 if (buf->b_spell)
756 {
Bram Moolenaare5c421c2015-03-31 13:33:08 +0200757 fname = vim_tempname('s', FALSE);
Bram Moolenaar4770d092006-01-12 23:22:24 +0000758 if (fname != NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100759 (void)mf_open_file(mfp, fname); // consumes fname!
Bram Moolenaar4770d092006-01-12 23:22:24 +0000760 buf->b_may_swap = FALSE;
761 return;
762 }
763#endif
764
Bram Moolenaar071d4272004-06-13 20:20:40 +0000765 /*
766 * Try all directories in 'directory' option.
767 */
768 dirp = p_dir;
769 for (;;)
770 {
771 if (*dirp == NUL)
772 break;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100773 // There is a small chance that between choosing the swap file name
774 // and creating it, another Vim creates the file. In that case the
775 // creation will fail and we will use another directory.
776 fname = findswapname(buf, &dirp, NULL); // allocates fname
Bram Moolenaarf541c362011-10-26 11:44:18 +0200777 if (dirp == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100778 break; // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +0000779 if (fname == NULL)
780 continue;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100781 if (mf_open_file(mfp, fname) == OK) // consumes fname!
Bram Moolenaar071d4272004-06-13 20:20:40 +0000782 {
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100783#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000784 /*
785 * set full pathname for swap file now, because a ":!cd dir" may
786 * change directory without us knowing it.
787 */
788 mf_fullname(mfp);
789#endif
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200790 ml_upd_block0(buf, UB_SAME_DIR);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000791
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100792 // Flush block zero, so others can read it
Bram Moolenaar071d4272004-06-13 20:20:40 +0000793 if (mf_sync(mfp, MFS_ZERO) == OK)
Bram Moolenaarc32840f2006-01-14 21:23:38 +0000794 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100795 // Mark all blocks that should be in the swapfile as dirty.
796 // Needed for when the 'swapfile' option was reset, so that
797 // the swap file was deleted, and then on again.
Bram Moolenaarc32840f2006-01-14 21:23:38 +0000798 mf_set_dirty(mfp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000799 break;
Bram Moolenaarc32840f2006-01-14 21:23:38 +0000800 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100801 // Writing block 0 failed: close the file and try another dir
Bram Moolenaar071d4272004-06-13 20:20:40 +0000802 mf_close_file(buf, FALSE);
803 }
804 }
805
Bram Moolenaar00e192b2019-10-19 17:01:28 +0200806 if (*p_dir != NUL && mfp->mf_fname == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000807 {
Bram Moolenaar00e192b2019-10-19 17:01:28 +0200808 need_wait_return = TRUE; // call wait_return later
Bram Moolenaar071d4272004-06-13 20:20:40 +0000809 ++no_wait_return;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100810 (void)semsg(_("E303: Unable to open swap file for \"%s\", recovery impossible"),
Bram Moolenaare1704ba2012-10-03 18:25:00 +0200811 buf_spname(buf) != NULL ? buf_spname(buf) : buf->b_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000812 --no_wait_return;
813 }
814
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100815 // don't try to open a swap file again
Bram Moolenaar071d4272004-06-13 20:20:40 +0000816 buf->b_may_swap = FALSE;
817}
818
819/*
820 * If still need to create a swap file, and starting to edit a not-readonly
821 * file, or reading into an existing buffer, create a swap file now.
822 */
823 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100824check_need_swap(
Bram Moolenaar2f0f8712018-08-21 18:50:18 +0200825 int newfile) // reading file into new buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +0000826{
Bram Moolenaar2f0f8712018-08-21 18:50:18 +0200827 int old_msg_silent = msg_silent; // might be reset by an E325 message
828
Bram Moolenaar071d4272004-06-13 20:20:40 +0000829 if (curbuf->b_may_swap && (!curbuf->b_p_ro || !newfile))
830 ml_open_file(curbuf);
Bram Moolenaar2f0f8712018-08-21 18:50:18 +0200831 msg_silent = old_msg_silent;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000832}
833
834/*
835 * Close memline for buffer 'buf'.
836 * If 'del_file' is TRUE, delete the swap file
837 */
838 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100839ml_close(buf_T *buf, int del_file)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000840{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100841 if (buf->b_ml.ml_mfp == NULL) // not open
Bram Moolenaar071d4272004-06-13 20:20:40 +0000842 return;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100843 mf_close(buf->b_ml.ml_mfp, del_file); // close the .swp file
Bram Moolenaar071d4272004-06-13 20:20:40 +0000844 if (buf->b_ml.ml_line_lnum != 0 && (buf->b_ml.ml_flags & ML_LINE_DIRTY))
845 vim_free(buf->b_ml.ml_line_ptr);
846 vim_free(buf->b_ml.ml_stack);
847#ifdef FEAT_BYTEOFF
Bram Moolenaard23a8232018-02-10 18:45:26 +0100848 VIM_CLEAR(buf->b_ml.ml_chunksize);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000849#endif
850 buf->b_ml.ml_mfp = NULL;
851
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100852 // Reset the "recovered" flag, give the ATTENTION prompt the next time
853 // this buffer is loaded.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000854 buf->b_flags &= ~BF_RECOVERED;
855}
856
857/*
858 * Close all existing memlines and memfiles.
859 * Only used when exiting.
860 * When 'del_file' is TRUE, delete the memfiles.
Bram Moolenaar81bf7082005-02-12 14:31:42 +0000861 * But don't delete files that were ":preserve"d when we are POSIX compatible.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000862 */
863 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100864ml_close_all(int del_file)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000865{
866 buf_T *buf;
867
Bram Moolenaar29323592016-07-24 22:04:11 +0200868 FOR_ALL_BUFFERS(buf)
Bram Moolenaar81bf7082005-02-12 14:31:42 +0000869 ml_close(buf, del_file && ((buf->b_flags & BF_PRESERVED) == 0
870 || vim_strchr(p_cpo, CPO_PRESERVE) == NULL));
Bram Moolenaar34b466e2013-11-28 17:41:46 +0100871#ifdef FEAT_SPELL
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100872 spell_delete_wordlist(); // delete the internal wordlist
Bram Moolenaar34b466e2013-11-28 17:41:46 +0100873#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000874#ifdef TEMPDIRNAMES
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100875 vim_deltempdir(); // delete created temp directory
Bram Moolenaar071d4272004-06-13 20:20:40 +0000876#endif
877}
878
879/*
880 * Close all memfiles for not modified buffers.
881 * Only use just before exiting!
882 */
883 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100884ml_close_notmod(void)
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 Moolenaar071d4272004-06-13 20:20:40 +0000889 if (!bufIsChanged(buf))
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100890 ml_close(buf, TRUE); // close all not-modified buffers
Bram Moolenaar071d4272004-06-13 20:20:40 +0000891}
892
893/*
894 * Update the timestamp in the .swp file.
895 * Used when the file has been written.
896 */
897 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100898ml_timestamp(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000899{
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200900 ml_upd_block0(buf, UB_FNAME);
901}
902
903/*
904 * Return FAIL when the ID of "b0p" is wrong.
905 */
906 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100907ml_check_b0_id(ZERO_BL *b0p)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200908{
909 if (b0p->b0_id[0] != BLOCK0_ID0
910 || (b0p->b0_id[1] != BLOCK0_ID1
911 && b0p->b0_id[1] != BLOCK0_ID1_C0
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200912 && b0p->b0_id[1] != BLOCK0_ID1_C1
913 && b0p->b0_id[1] != BLOCK0_ID1_C2)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200914 )
915 return FAIL;
916 return OK;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000917}
918
919/*
920 * Update the timestamp or the B0_SAME_DIR flag of the .swp file.
921 */
922 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100923ml_upd_block0(buf_T *buf, upd_block0_T what)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000924{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000925 memfile_T *mfp;
926 bhdr_T *hp;
927 ZERO_BL *b0p;
928
929 mfp = buf->b_ml.ml_mfp;
Bram Moolenaar2be79502014-08-13 21:58:28 +0200930 if (mfp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000931 return;
Bram Moolenaar2be79502014-08-13 21:58:28 +0200932 hp = mf_get(mfp, (blocknr_T)0, 1);
933 if (hp == NULL)
934 {
935#ifdef FEAT_CRYPT
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100936 // Possibly update the seed in the memfile before there is a block0.
Bram Moolenaar2be79502014-08-13 21:58:28 +0200937 if (what == UB_CRYPT)
938 ml_set_mfp_crypt(buf);
939#endif
940 return;
941 }
942
Bram Moolenaar071d4272004-06-13 20:20:40 +0000943 b0p = (ZERO_BL *)(hp->bh_data);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200944 if (ml_check_b0_id(b0p) == FAIL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100945 iemsg(_("E304: ml_upd_block0(): Didn't get block 0??"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000946 else
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000947 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200948 if (what == UB_FNAME)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000949 set_b0_fname(b0p, buf);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200950#ifdef FEAT_CRYPT
951 else if (what == UB_CRYPT)
952 ml_set_b0_crypt(buf, b0p);
953#endif
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100954 else // what == UB_SAME_DIR
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000955 set_b0_dir_flag(b0p, buf);
956 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000957 mf_put(mfp, hp, TRUE, FALSE);
958}
959
960/*
961 * Write file name and timestamp into block 0 of a swap file.
962 * Also set buf->b_mtime.
963 * Don't use NameBuff[]!!!
964 */
965 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100966set_b0_fname(ZERO_BL *b0p, buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000967{
Bram Moolenaar8767f522016-07-01 17:17:39 +0200968 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000969
970 if (buf->b_ffname == NULL)
971 b0p->b0_fname[0] = NUL;
972 else
973 {
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100974#if defined(MSWIN) || defined(AMIGA)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100975 // Systems that cannot translate "~user" back into a path: copy the
976 // file name unmodified. Do use slashes instead of backslashes for
977 // portability.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200978 vim_strncpy(b0p->b0_fname, buf->b_ffname, B0_FNAME_SIZE_CRYPT - 1);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000979# ifdef BACKSLASH_IN_FILENAME
980 forward_slash(b0p->b0_fname);
981# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000982#else
983 size_t flen, ulen;
984 char_u uname[B0_UNAME_SIZE];
985
986 /*
987 * For a file under the home directory of the current user, we try to
988 * replace the home directory path with "~user". This helps when
989 * editing the same file on different machines over a network.
990 * First replace home dir path with "~/" with home_replace().
991 * Then insert the user name to get "~user/".
992 */
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200993 home_replace(NULL, buf->b_ffname, b0p->b0_fname,
994 B0_FNAME_SIZE_CRYPT, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000995 if (b0p->b0_fname[0] == '~')
996 {
997 flen = STRLEN(b0p->b0_fname);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100998 // If there is no user name or it is too long, don't use "~/"
Bram Moolenaar071d4272004-06-13 20:20:40 +0000999 if (get_user_name(uname, B0_UNAME_SIZE) == FAIL
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001000 || (ulen = STRLEN(uname)) + flen > B0_FNAME_SIZE_CRYPT - 1)
1001 vim_strncpy(b0p->b0_fname, buf->b_ffname,
1002 B0_FNAME_SIZE_CRYPT - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001003 else
1004 {
1005 mch_memmove(b0p->b0_fname + ulen + 1, b0p->b0_fname + 1, flen);
1006 mch_memmove(b0p->b0_fname + 1, uname, ulen);
1007 }
1008 }
1009#endif
1010 if (mch_stat((char *)buf->b_ffname, &st) >= 0)
1011 {
1012 long_to_char((long)st.st_mtime, b0p->b0_mtime);
1013#ifdef CHECK_INODE
1014 long_to_char((long)st.st_ino, b0p->b0_ino);
1015#endif
1016 buf_store_time(buf, &st, buf->b_ffname);
1017 buf->b_mtime_read = buf->b_mtime;
1018 }
1019 else
1020 {
1021 long_to_char(0L, b0p->b0_mtime);
1022#ifdef CHECK_INODE
1023 long_to_char(0L, b0p->b0_ino);
1024#endif
1025 buf->b_mtime = 0;
1026 buf->b_mtime_read = 0;
1027 buf->b_orig_size = 0;
1028 buf->b_orig_mode = 0;
1029 }
1030 }
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001031
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001032 // Also add the 'fileencoding' if there is room.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001033 add_b0_fenc(b0p, curbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001034}
1035
1036/*
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001037 * Update the B0_SAME_DIR flag of the swap file. It's set if the file and the
1038 * swapfile for "buf" are in the same directory.
1039 * This is fail safe: if we are not sure the directories are equal the flag is
1040 * not set.
1041 */
1042 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001043set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001044{
1045 if (same_directory(buf->b_ml.ml_mfp->mf_fname, buf->b_ffname))
1046 b0p->b0_flags |= B0_SAME_DIR;
1047 else
1048 b0p->b0_flags &= ~B0_SAME_DIR;
1049}
1050
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001051/*
1052 * When there is room, add the 'fileencoding' to block zero.
1053 */
1054 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001055add_b0_fenc(
1056 ZERO_BL *b0p,
1057 buf_T *buf)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001058{
1059 int n;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001060 int size = B0_FNAME_SIZE_NOCRYPT;
1061
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01001062#ifdef FEAT_CRYPT
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001063 // Without encryption use the same offset as in Vim 7.2 to be compatible.
1064 // With encryption it's OK to move elsewhere, the swap file is not
1065 // compatible anyway.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001066 if (*buf->b_p_key != NUL)
1067 size = B0_FNAME_SIZE_CRYPT;
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01001068#endif
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001069
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00001070 n = (int)STRLEN(buf->b_p_fenc);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001071 if ((int)STRLEN(b0p->b0_fname) + n + 1 > size)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001072 b0p->b0_flags &= ~B0_HAS_FENC;
1073 else
1074 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001075 mch_memmove((char *)b0p->b0_fname + size - n,
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001076 (char *)buf->b_p_fenc, (size_t)n);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001077 *(b0p->b0_fname + size - n - 1) = NUL;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001078 b0p->b0_flags |= B0_HAS_FENC;
1079 }
1080}
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001081
1082
1083/*
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001084 * Try to recover curbuf from the .swp file.
Bram Moolenaar99499b12019-05-23 21:35:48 +02001085 * If "checkext" is TRUE, check the extension and detect whether it is
1086 * a swap file.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001087 */
1088 void
Bram Moolenaar99499b12019-05-23 21:35:48 +02001089ml_recover(int checkext)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001090{
1091 buf_T *buf = NULL;
1092 memfile_T *mfp = NULL;
1093 char_u *fname;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001094 char_u *fname_used = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001095 bhdr_T *hp = NULL;
1096 ZERO_BL *b0p;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001097 int b0_ff;
1098 char_u *b0_fenc = NULL;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001099#ifdef FEAT_CRYPT
1100 int b0_cm = -1;
1101#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001102 PTR_BL *pp;
1103 DATA_BL *dp;
1104 infoptr_T *ip;
1105 blocknr_T bnum;
1106 int page_count;
Bram Moolenaar8767f522016-07-01 17:17:39 +02001107 stat_T org_stat, swp_stat;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001108 int len;
1109 int directly;
1110 linenr_T lnum;
1111 char_u *p;
1112 int i;
1113 long error;
1114 int cannot_open;
1115 linenr_T line_count;
1116 int has_error;
1117 int idx;
1118 int top;
1119 int txt_start;
Bram Moolenaar8767f522016-07-01 17:17:39 +02001120 off_T size;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001121 int called_from_main;
1122 int serious_error = TRUE;
1123 long mtime;
1124 int attr;
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001125 int orig_file_status = NOTDONE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001126
1127 recoverymode = TRUE;
1128 called_from_main = (curbuf->b_ml.ml_mfp == NULL);
Bram Moolenaar8820b482017-03-16 17:23:31 +01001129 attr = HL_ATTR(HLF_E);
Bram Moolenaard0ba34a2009-11-03 12:06:23 +00001130
1131 /*
Bram Moolenaaraf903e52017-12-02 15:11:22 +01001132 * 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 +00001133 * Otherwise a search is done to find the swap file(s).
1134 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001135 fname = curbuf->b_fname;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001136 if (fname == NULL) // When there is no file name
Bram Moolenaar071d4272004-06-13 20:20:40 +00001137 fname = (char_u *)"";
1138 len = (int)STRLEN(fname);
Bram Moolenaar99499b12019-05-23 21:35:48 +02001139 if (checkext && len >= 4 &&
Bram Moolenaare60acc12011-05-10 16:41:25 +02001140#if defined(VMS)
Bram Moolenaar79518e22017-02-17 16:31:35 +01001141 STRNICMP(fname + len - 4, "_s", 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001142#else
Bram Moolenaar79518e22017-02-17 16:31:35 +01001143 STRNICMP(fname + len - 4, ".s", 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001144#endif
Bram Moolenaar79518e22017-02-17 16:31:35 +01001145 == 0
Bram Moolenaaraf903e52017-12-02 15:11:22 +01001146 && vim_strchr((char_u *)"abcdefghijklmnopqrstuvw",
1147 TOLOWER_ASC(fname[len - 2])) != NULL
Bram Moolenaard0ba34a2009-11-03 12:06:23 +00001148 && ASCII_ISALPHA(fname[len - 1]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001149 {
1150 directly = TRUE;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001151 fname_used = vim_strsave(fname); // make a copy for mf_open()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001152 }
1153 else
1154 {
1155 directly = FALSE;
1156
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001157 // count the number of matching swap files
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001158 len = recover_names(fname, FALSE, 0, NULL);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001159 if (len == 0) // no swap files found
Bram Moolenaar071d4272004-06-13 20:20:40 +00001160 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001161 semsg(_("E305: No swap file found for %s"), fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001162 goto theend;
1163 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001164 if (len == 1) // one swap file found, use it
Bram Moolenaar071d4272004-06-13 20:20:40 +00001165 i = 1;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001166 else // several swap files found, choose
Bram Moolenaar071d4272004-06-13 20:20:40 +00001167 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001168 // list the names of the swap files
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001169 (void)recover_names(fname, TRUE, 0, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001170 msg_putchar('\n');
Bram Moolenaar32526b32019-01-19 17:43:09 +01001171 msg_puts(_("Enter number of swap file to use (0 to quit): "));
Bram Moolenaar24bbcfe2005-06-28 23:32:02 +00001172 i = get_number(FALSE, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001173 if (i < 1 || i > len)
1174 goto theend;
1175 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001176 // get the swap file name that will be used
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001177 (void)recover_names(fname, FALSE, i, &fname_used);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001178 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001179 if (fname_used == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001180 goto theend; // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00001181
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001182 // When called from main() still need to initialize storage structure
Bram Moolenaar4770d092006-01-12 23:22:24 +00001183 if (called_from_main && ml_open(curbuf) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001184 getout(1);
1185
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001186 /*
1187 * Allocate a buffer structure for the swap file that is used for recovery.
Bram Moolenaar0ad014c2010-07-25 14:00:46 +02001188 * Only the memline and crypt information in it are really used.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001189 */
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001190 buf = ALLOC_ONE(buf_T);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001191 if (buf == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001192 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001193
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001194 /*
1195 * init fields in memline struct
1196 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001197 buf->b_ml.ml_stack_size = 0; // no stack yet
1198 buf->b_ml.ml_stack = NULL; // no stack yet
1199 buf->b_ml.ml_stack_top = 0; // nothing in the stack
1200 buf->b_ml.ml_line_lnum = 0; // no cached line
1201 buf->b_ml.ml_locked = NULL; // no locked block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001202 buf->b_ml.ml_flags = 0;
Bram Moolenaar0fe849a2010-07-25 15:11:11 +02001203#ifdef FEAT_CRYPT
1204 buf->b_p_key = empty_option;
1205 buf->b_p_cm = empty_option;
1206#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001207
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001208 /*
1209 * open the memfile from the old swap file
1210 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001211 p = vim_strsave(fname_used); // save "fname_used" for the message:
1212 // mf_open() will consume "fname_used"!
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001213 mfp = mf_open(fname_used, O_RDONLY);
1214 fname_used = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001215 if (mfp == NULL || mfp->mf_fd < 0)
1216 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001217 if (fname_used != NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001218 semsg(_("E306: Cannot open %s"), fname_used);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001219 goto theend;
1220 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001221 buf->b_ml.ml_mfp = mfp;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001222#ifdef FEAT_CRYPT
1223 mfp->mf_buffer = buf;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001224#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001225
1226 /*
1227 * The page size set in mf_open() might be different from the page size
1228 * used in the swap file, we must get it from block 0. But to read block
1229 * 0 we need a page size. Use the minimal size for block 0 here, it will
1230 * be set to the real value below.
1231 */
1232 mfp->mf_page_size = MIN_SWAP_PAGE_SIZE;
1233
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001234 /*
1235 * try to read block 0
1236 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001237 if ((hp = mf_get(mfp, (blocknr_T)0, 1)) == NULL)
1238 {
1239 msg_start();
Bram Moolenaar32526b32019-01-19 17:43:09 +01001240 msg_puts_attr(_("Unable to read block 0 from "), attr | MSG_HIST);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001241 msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001242 msg_puts_attr(_("\nMaybe no changes were made or Vim did not update the swap file."),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001243 attr | MSG_HIST);
1244 msg_end();
1245 goto theend;
1246 }
1247 b0p = (ZERO_BL *)(hp->bh_data);
1248 if (STRNCMP(b0p->b0_version, "VIM 3.0", 7) == 0)
1249 {
1250 msg_start();
1251 msg_outtrans_attr(mfp->mf_fname, MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001252 msg_puts_attr(_(" cannot be used with this version of Vim.\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001253 MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001254 msg_puts_attr(_("Use Vim version 3.0.\n"), MSG_HIST);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001255 msg_end();
1256 goto theend;
1257 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001258 if (ml_check_b0_id(b0p) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001259 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001260 semsg(_("E307: %s does not look like a Vim swap file"), mfp->mf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001261 goto theend;
1262 }
1263 if (b0_magic_wrong(b0p))
1264 {
1265 msg_start();
1266 msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
Bram Moolenaar48e330a2016-02-23 14:53:34 +01001267#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001268 if (STRNCMP(b0p->b0_hname, "PC ", 3) == 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001269 msg_puts_attr(_(" cannot be used with this version of Vim.\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001270 attr | MSG_HIST);
1271 else
1272#endif
Bram Moolenaar32526b32019-01-19 17:43:09 +01001273 msg_puts_attr(_(" cannot be used on this computer.\n"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001274 attr | MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001275 msg_puts_attr(_("The file was created on "), attr | MSG_HIST);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001276 // avoid going past the end of a corrupted hostname
Bram Moolenaar071d4272004-06-13 20:20:40 +00001277 b0p->b0_fname[0] = NUL;
Bram Moolenaar32526b32019-01-19 17:43:09 +01001278 msg_puts_attr((char *)b0p->b0_hname, attr | MSG_HIST);
1279 msg_puts_attr(_(",\nor the file has been damaged."), attr | MSG_HIST);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001280 msg_end();
1281 goto theend;
1282 }
Bram Moolenaar1c536282007-04-26 15:21:56 +00001283
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001284#ifdef FEAT_CRYPT
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001285 for (i = 0; i < (int)(sizeof(id1_codes) / sizeof(int)); ++i)
1286 if (id1_codes[i] == b0p->b0_id[1])
1287 b0_cm = i;
1288 if (b0_cm > 0)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001289 mch_memmove(mfp->mf_seed, &b0p->b0_seed, MF_SEED_LEN);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001290 crypt_set_cm_option(buf, b0_cm < 0 ? 0 : b0_cm);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001291#else
1292 if (b0p->b0_id[1] != BLOCK0_ID1)
1293 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001294 semsg(_("E833: %s is encrypted and this version of Vim does not support encryption"), mfp->mf_fname);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001295 goto theend;
1296 }
1297#endif
1298
Bram Moolenaar071d4272004-06-13 20:20:40 +00001299 /*
1300 * If we guessed the wrong page size, we have to recalculate the
1301 * highest block number in the file.
1302 */
1303 if (mfp->mf_page_size != (unsigned)char_to_long(b0p->b0_page_size))
1304 {
Bram Moolenaar1c536282007-04-26 15:21:56 +00001305 unsigned previous_page_size = mfp->mf_page_size;
1306
Bram Moolenaar071d4272004-06-13 20:20:40 +00001307 mf_new_page_size(mfp, (unsigned)char_to_long(b0p->b0_page_size));
Bram Moolenaar1c536282007-04-26 15:21:56 +00001308 if (mfp->mf_page_size < previous_page_size)
1309 {
1310 msg_start();
1311 msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001312 msg_puts_attr(_(" has been damaged (page size is smaller than minimum value).\n"),
Bram Moolenaar1c536282007-04-26 15:21:56 +00001313 attr | MSG_HIST);
1314 msg_end();
1315 goto theend;
1316 }
Bram Moolenaar8767f522016-07-01 17:17:39 +02001317 if ((size = vim_lseek(mfp->mf_fd, (off_T)0L, SEEK_END)) <= 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001318 mfp->mf_blocknr_max = 0; // no file or empty file
Bram Moolenaar071d4272004-06-13 20:20:40 +00001319 else
1320 mfp->mf_blocknr_max = (blocknr_T)(size / mfp->mf_page_size);
1321 mfp->mf_infile_count = mfp->mf_blocknr_max;
Bram Moolenaar1c536282007-04-26 15:21:56 +00001322
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001323 // need to reallocate the memory used to store the data
Bram Moolenaar1c536282007-04-26 15:21:56 +00001324 p = alloc(mfp->mf_page_size);
1325 if (p == NULL)
1326 goto theend;
1327 mch_memmove(p, hp->bh_data, previous_page_size);
1328 vim_free(hp->bh_data);
1329 hp->bh_data = p;
1330 b0p = (ZERO_BL *)(hp->bh_data);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001331 }
1332
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001333 /*
1334 * If .swp file name given directly, use name from swap file for buffer.
1335 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001336 if (directly)
1337 {
1338 expand_env(b0p->b0_fname, NameBuff, MAXPATHL);
1339 if (setfname(curbuf, NameBuff, NULL, TRUE) == FAIL)
1340 goto theend;
1341 }
1342
1343 home_replace(NULL, mfp->mf_fname, NameBuff, MAXPATHL, TRUE);
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001344 smsg(_("Using swap file \"%s\""), NameBuff);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001345
1346 if (buf_spname(curbuf) != NULL)
Bram Moolenaare1704ba2012-10-03 18:25:00 +02001347 vim_strncpy(NameBuff, buf_spname(curbuf), MAXPATHL - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001348 else
1349 home_replace(NULL, curbuf->b_ffname, NameBuff, MAXPATHL, TRUE);
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001350 smsg(_("Original file \"%s\""), NameBuff);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001351 msg_putchar('\n');
1352
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001353 /*
1354 * check date of swap file and original file
1355 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001356 mtime = char_to_long(b0p->b0_mtime);
1357 if (curbuf->b_ffname != NULL
1358 && mch_stat((char *)curbuf->b_ffname, &org_stat) != -1
1359 && ((mch_stat((char *)mfp->mf_fname, &swp_stat) != -1
1360 && org_stat.st_mtime > swp_stat.st_mtime)
1361 || org_stat.st_mtime != mtime))
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001362 emsg(_("E308: Warning: Original file may have been changed"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001363 out_flush();
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001364
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001365 // Get the 'fileformat' and 'fileencoding' from block zero.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001366 b0_ff = (b0p->b0_flags & B0_FF_MASK);
1367 if (b0p->b0_flags & B0_HAS_FENC)
1368 {
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001369 int fnsize = B0_FNAME_SIZE_NOCRYPT;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001370
1371#ifdef FEAT_CRYPT
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001372 // Use the same size as in add_b0_fenc().
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001373 if (b0p->b0_id[1] != BLOCK0_ID1)
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001374 fnsize = B0_FNAME_SIZE_CRYPT;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001375#endif
Bram Moolenaarf506c5b2010-06-22 06:28:58 +02001376 for (p = b0p->b0_fname + fnsize; p > b0p->b0_fname && p[-1] != NUL; --p)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001377 ;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001378 b0_fenc = vim_strnsave(p, b0p->b0_fname + fnsize - p);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001379 }
1380
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001381 mf_put(mfp, hp, FALSE, FALSE); // release block 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001382 hp = NULL;
1383
1384 /*
1385 * Now that we are sure that the file is going to be recovered, clear the
1386 * contents of the current buffer.
1387 */
1388 while (!(curbuf->b_ml.ml_flags & ML_EMPTY))
Bram Moolenaarca70c072020-05-30 20:30:46 +02001389 ml_delete((linenr_T)1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001390
1391 /*
1392 * Try reading the original file to obtain the values of 'fileformat',
1393 * 'fileencoding', etc. Ignore errors. The text itself is not used.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001394 * When the file is encrypted the user is asked to enter the key.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001395 */
1396 if (curbuf->b_ffname != NULL)
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001397 orig_file_status = readfile(curbuf->b_ffname, NULL, (linenr_T)0,
Bram Moolenaar071d4272004-06-13 20:20:40 +00001398 (linenr_T)0, (linenr_T)MAXLNUM, NULL, READ_NEW);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001399
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001400#ifdef FEAT_CRYPT
1401 if (b0_cm >= 0)
1402 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001403 // Need to ask the user for the crypt key. If this fails we continue
1404 // without a key, will probably get garbage text.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001405 if (*curbuf->b_p_key != NUL)
1406 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001407 smsg(_("Swap file is encrypted: \"%s\""), fname_used);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001408 msg_puts(_("\nIf you entered a new crypt key but did not write the text file,"));
1409 msg_puts(_("\nenter the new crypt key."));
1410 msg_puts(_("\nIf you wrote the text file after changing the crypt key press enter"));
1411 msg_puts(_("\nto use the same key for text file and swap file"));
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001412 }
1413 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001414 smsg(_(need_key_msg), fname_used);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02001415 buf->b_p_key = crypt_get_key(FALSE, FALSE);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001416 if (buf->b_p_key == NULL)
1417 buf->b_p_key = curbuf->b_p_key;
1418 else if (*buf->b_p_key == NUL)
1419 {
1420 vim_free(buf->b_p_key);
1421 buf->b_p_key = curbuf->b_p_key;
1422 }
1423 if (buf->b_p_key == NULL)
1424 buf->b_p_key = empty_option;
1425 }
1426#endif
1427
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001428 // Use the 'fileformat' and 'fileencoding' as stored in the swap file.
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001429 if (b0_ff != 0)
1430 set_fileformat(b0_ff - 1, OPT_LOCAL);
1431 if (b0_fenc != NULL)
1432 {
1433 set_option_value((char_u *)"fenc", 0L, b0_fenc, OPT_LOCAL);
1434 vim_free(b0_fenc);
1435 }
Bram Moolenaarc024b462019-06-08 18:07:21 +02001436 unchanged(curbuf, TRUE, TRUE);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001437
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001438 bnum = 1; // start with block 1
1439 page_count = 1; // which is 1 page
1440 lnum = 0; // append after line 0 in curbuf
Bram Moolenaar071d4272004-06-13 20:20:40 +00001441 line_count = 0;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001442 idx = 0; // start with first index in block 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00001443 error = 0;
1444 buf->b_ml.ml_stack_top = 0;
1445 buf->b_ml.ml_stack = NULL;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001446 buf->b_ml.ml_stack_size = 0; // no stack yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00001447
1448 if (curbuf->b_ffname == NULL)
1449 cannot_open = TRUE;
1450 else
1451 cannot_open = FALSE;
1452
1453 serious_error = FALSE;
1454 for ( ; !got_int; line_breakcheck())
1455 {
1456 if (hp != NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001457 mf_put(mfp, hp, FALSE, FALSE); // release previous block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001458
1459 /*
1460 * get block
1461 */
1462 if ((hp = mf_get(mfp, (blocknr_T)bnum, page_count)) == NULL)
1463 {
1464 if (bnum == 1)
1465 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001466 semsg(_("E309: Unable to read block 1 from %s"), mfp->mf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001467 goto theend;
1468 }
1469 ++error;
1470 ml_append(lnum++, (char_u *)_("???MANY LINES MISSING"),
1471 (colnr_T)0, TRUE);
1472 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001473 else // there is a block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001474 {
1475 pp = (PTR_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001476 if (pp->pb_id == PTR_ID) // it is a pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001477 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001478 // check line count when using pointer block first time
Bram Moolenaar071d4272004-06-13 20:20:40 +00001479 if (idx == 0 && line_count != 0)
1480 {
1481 for (i = 0; i < (int)pp->pb_count; ++i)
1482 line_count -= pp->pb_pointer[i].pe_line_count;
1483 if (line_count != 0)
1484 {
1485 ++error;
1486 ml_append(lnum++, (char_u *)_("???LINE COUNT WRONG"),
1487 (colnr_T)0, TRUE);
1488 }
1489 }
1490
1491 if (pp->pb_count == 0)
1492 {
1493 ml_append(lnum++, (char_u *)_("???EMPTY BLOCK"),
1494 (colnr_T)0, TRUE);
1495 ++error;
1496 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001497 else if (idx < (int)pp->pb_count) // go a block deeper
Bram Moolenaar071d4272004-06-13 20:20:40 +00001498 {
1499 if (pp->pb_pointer[idx].pe_bnum < 0)
1500 {
1501 /*
1502 * Data block with negative block number.
1503 * Try to read lines from the original file.
1504 * This is slow, but it works.
1505 */
1506 if (!cannot_open)
1507 {
1508 line_count = pp->pb_pointer[idx].pe_line_count;
1509 if (readfile(curbuf->b_ffname, NULL, lnum,
1510 pp->pb_pointer[idx].pe_old_lnum - 1,
Bram Moolenaare13b9af2017-01-13 22:01:02 +01001511 line_count, NULL, 0) != OK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001512 cannot_open = TRUE;
1513 else
1514 lnum += line_count;
1515 }
1516 if (cannot_open)
1517 {
1518 ++error;
1519 ml_append(lnum++, (char_u *)_("???LINES MISSING"),
1520 (colnr_T)0, TRUE);
1521 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001522 ++idx; // get same block again for next index
Bram Moolenaar071d4272004-06-13 20:20:40 +00001523 continue;
1524 }
1525
1526 /*
1527 * going one block deeper in the tree
1528 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001529 if ((top = ml_add_stack(buf)) < 0) // new entry in stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00001530 {
1531 ++error;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001532 break; // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00001533 }
1534 ip = &(buf->b_ml.ml_stack[top]);
1535 ip->ip_bnum = bnum;
1536 ip->ip_index = idx;
1537
1538 bnum = pp->pb_pointer[idx].pe_bnum;
1539 line_count = pp->pb_pointer[idx].pe_line_count;
1540 page_count = pp->pb_pointer[idx].pe_page_count;
Bram Moolenaar986a0032011-06-13 01:07:27 +02001541 idx = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001542 continue;
1543 }
1544 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001545 else // not a pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001546 {
1547 dp = (DATA_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001548 if (dp->db_id != DATA_ID) // block id wrong
Bram Moolenaar071d4272004-06-13 20:20:40 +00001549 {
1550 if (bnum == 1)
1551 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001552 semsg(_("E310: Block 1 ID wrong (%s not a .swp file?)"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00001553 mfp->mf_fname);
1554 goto theend;
1555 }
1556 ++error;
1557 ml_append(lnum++, (char_u *)_("???BLOCK MISSING"),
1558 (colnr_T)0, TRUE);
1559 }
1560 else
1561 {
1562 /*
1563 * it is a data block
1564 * Append all the lines in this block
1565 */
1566 has_error = FALSE;
1567 /*
1568 * check length of block
1569 * if wrong, use length in pointer block
1570 */
1571 if (page_count * mfp->mf_page_size != dp->db_txt_end)
1572 {
1573 ml_append(lnum++, (char_u *)_("??? from here until ???END lines may be messed up"),
1574 (colnr_T)0, TRUE);
1575 ++error;
1576 has_error = TRUE;
1577 dp->db_txt_end = page_count * mfp->mf_page_size;
1578 }
1579
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001580 // make sure there is a NUL at the end of the block
Bram Moolenaar071d4272004-06-13 20:20:40 +00001581 *((char_u *)dp + dp->db_txt_end - 1) = NUL;
1582
1583 /*
1584 * check number of lines in block
1585 * if wrong, use count in data block
1586 */
1587 if (line_count != dp->db_line_count)
1588 {
1589 ml_append(lnum++, (char_u *)_("??? from here until ???END lines may have been inserted/deleted"),
1590 (colnr_T)0, TRUE);
1591 ++error;
1592 has_error = TRUE;
1593 }
1594
1595 for (i = 0; i < dp->db_line_count; ++i)
1596 {
1597 txt_start = (dp->db_index[i] & DB_INDEX_MASK);
Bram Moolenaar740885b2009-11-03 14:33:17 +00001598 if (txt_start <= (int)HEADER_SIZE
Bram Moolenaar071d4272004-06-13 20:20:40 +00001599 || txt_start >= (int)dp->db_txt_end)
1600 {
1601 p = (char_u *)"???";
1602 ++error;
1603 }
1604 else
1605 p = (char_u *)dp + txt_start;
1606 ml_append(lnum++, p, (colnr_T)0, TRUE);
1607 }
1608 if (has_error)
Bram Moolenaar740885b2009-11-03 14:33:17 +00001609 ml_append(lnum++, (char_u *)_("???END"),
1610 (colnr_T)0, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001611 }
1612 }
1613 }
1614
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001615 if (buf->b_ml.ml_stack_top == 0) // finished
Bram Moolenaar071d4272004-06-13 20:20:40 +00001616 break;
1617
1618 /*
1619 * go one block up in the tree
1620 */
1621 ip = &(buf->b_ml.ml_stack[--(buf->b_ml.ml_stack_top)]);
1622 bnum = ip->ip_bnum;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001623 idx = ip->ip_index + 1; // go to next index
Bram Moolenaar071d4272004-06-13 20:20:40 +00001624 page_count = 1;
1625 }
1626
1627 /*
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001628 * Compare the buffer contents with the original file. When they differ
1629 * set the 'modified' flag.
1630 * Lines 1 - lnum are the new contents.
1631 * Lines lnum + 1 to ml_line_count are the original contents.
1632 * Line ml_line_count + 1 in the dummy empty line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001633 */
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001634 if (orig_file_status != OK || curbuf->b_ml.ml_line_count != lnum * 2 + 1)
1635 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001636 // Recovering an empty file results in two lines and the first line is
1637 // empty. Don't set the modified flag then.
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001638 if (!(curbuf->b_ml.ml_line_count == 2 && *ml_get(1) == NUL))
1639 {
Bram Moolenaarec28d152019-05-11 18:36:34 +02001640 changed_internal();
Bram Moolenaar95c526e2017-02-25 14:59:34 +01001641 ++CHANGEDTICK(curbuf);
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001642 }
1643 }
1644 else
1645 {
1646 for (idx = 1; idx <= lnum; ++idx)
1647 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001648 // Need to copy one line, fetching the other one may flush it.
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001649 p = vim_strsave(ml_get(idx));
1650 i = STRCMP(p, ml_get(idx + lnum));
1651 vim_free(p);
1652 if (i != 0)
1653 {
Bram Moolenaarec28d152019-05-11 18:36:34 +02001654 changed_internal();
Bram Moolenaar95c526e2017-02-25 14:59:34 +01001655 ++CHANGEDTICK(curbuf);
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001656 break;
1657 }
1658 }
1659 }
1660
1661 /*
1662 * Delete the lines from the original file and the dummy line from the
1663 * empty buffer. These will now be after the last line in the buffer.
1664 */
1665 while (curbuf->b_ml.ml_line_count > lnum
1666 && !(curbuf->b_ml.ml_flags & ML_EMPTY))
Bram Moolenaarca70c072020-05-30 20:30:46 +02001667 ml_delete(curbuf->b_ml.ml_line_count);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001668 curbuf->b_flags |= BF_RECOVERED;
1669
1670 recoverymode = FALSE;
1671 if (got_int)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001672 emsg(_("E311: Recovery Interrupted"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001673 else if (error)
1674 {
1675 ++no_wait_return;
Bram Moolenaar32526b32019-01-19 17:43:09 +01001676 msg(">>>>>>>>>>>>>");
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001677 emsg(_("E312: Errors detected while recovering; look for lines starting with ???"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001678 --no_wait_return;
Bram Moolenaar32526b32019-01-19 17:43:09 +01001679 msg(_("See \":help E312\" for more information."));
1680 msg(">>>>>>>>>>>>>");
Bram Moolenaar071d4272004-06-13 20:20:40 +00001681 }
1682 else
1683 {
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001684 if (curbuf->b_changed)
1685 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001686 msg(_("Recovery completed. You should check if everything is OK."));
1687 msg_puts(_("\n(You might want to write out this file under another name\n"));
1688 msg_puts(_("and run diff with the original file to check for changes)"));
Bram Moolenaarfc2d5bd2010-05-15 17:06:53 +02001689 }
1690 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001691 msg(_("Recovery completed. Buffer contents equals file contents."));
1692 msg_puts(_("\nYou may want to delete the .swp file now.\n\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001693 cmdline_row = msg_row;
1694 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001695#ifdef FEAT_CRYPT
1696 if (*buf->b_p_key != NUL && STRCMP(curbuf->b_p_key, buf->b_p_key) != 0)
1697 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001698 msg_puts(_("Using crypt key from swap file for the text file.\n"));
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001699 set_option_value((char_u *)"key", 0L, buf->b_p_key, OPT_LOCAL);
1700 }
1701#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001702 redraw_curbuf_later(NOT_VALID);
1703
1704theend:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001705 vim_free(fname_used);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001706 recoverymode = FALSE;
1707 if (mfp != NULL)
1708 {
1709 if (hp != NULL)
1710 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001711 mf_close(mfp, FALSE); // will also vim_free(mfp->mf_fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001712 }
Bram Moolenaardf88dda2007-01-09 13:34:50 +00001713 if (buf != NULL)
1714 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001715#ifdef FEAT_CRYPT
1716 if (buf->b_p_key != curbuf->b_p_key)
1717 free_string_option(buf->b_p_key);
Bram Moolenaar0ad014c2010-07-25 14:00:46 +02001718 free_string_option(buf->b_p_cm);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001719#endif
Bram Moolenaardf88dda2007-01-09 13:34:50 +00001720 vim_free(buf->b_ml.ml_stack);
1721 vim_free(buf);
1722 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001723 if (serious_error && called_from_main)
1724 ml_close(curbuf, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001725 else
1726 {
1727 apply_autocmds(EVENT_BUFREADPOST, NULL, curbuf->b_fname, FALSE, curbuf);
1728 apply_autocmds(EVENT_BUFWINENTER, NULL, curbuf->b_fname, FALSE, curbuf);
1729 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001730 return;
1731}
1732
1733/*
1734 * Find the names of swap files in current directory and the directory given
1735 * with the 'directory' option.
1736 *
1737 * Used to:
1738 * - list the swap files for "vim -r"
1739 * - count the number of swap files when recovering
1740 * - list the swap files when recovering
1741 * - find the name of the n'th swap file when recovering
1742 */
1743 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001744recover_names(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001745 char_u *fname, // base for swap file name
1746 int list, // when TRUE, list the swap file names
1747 int nr, // when non-zero, return nr'th swap file name
1748 char_u **fname_out) // result when "nr" > 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00001749{
1750 int num_names;
1751 char_u *(names[6]);
1752 char_u *tail;
1753 char_u *p;
1754 int num_files;
1755 int file_count = 0;
1756 char_u **files;
1757 int i;
1758 char_u *dirp;
1759 char_u *dir_name;
Bram Moolenaar64354da2010-05-25 21:37:17 +02001760 char_u *fname_res = NULL;
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001761#ifdef HAVE_READLINK
1762 char_u fname_buf[MAXPATHL];
Bram Moolenaar64354da2010-05-25 21:37:17 +02001763#endif
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001764
Bram Moolenaar64354da2010-05-25 21:37:17 +02001765 if (fname != NULL)
1766 {
1767#ifdef HAVE_READLINK
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001768 // Expand symlink in the file name, because the swap file is created
1769 // with the actual file instead of with the symlink.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001770 if (resolve_symlink(fname, fname_buf) == OK)
1771 fname_res = fname_buf;
1772 else
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001773#endif
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001774 fname_res = fname;
Bram Moolenaar64354da2010-05-25 21:37:17 +02001775 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001776
1777 if (list)
1778 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001779 // use msg() to start the scrolling properly
Bram Moolenaar32526b32019-01-19 17:43:09 +01001780 msg(_("Swap files found:"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001781 msg_putchar('\n');
1782 }
1783
1784 /*
1785 * Do the loop for every directory in 'directory'.
1786 * First allocate some memory to put the directory name in.
1787 */
Bram Moolenaar964b3742019-05-24 18:54:09 +02001788 dir_name = alloc(STRLEN(p_dir) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001789 dirp = p_dir;
1790 while (dir_name != NULL && *dirp)
1791 {
1792 /*
1793 * Isolate a directory name from *dirp and put it in dir_name (we know
1794 * it is large enough, so use 31000 for length).
1795 * Advance dirp to next directory name.
1796 */
1797 (void)copy_option_part(&dirp, dir_name, 31000, ",");
1798
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001799 if (dir_name[0] == '.' && dir_name[1] == NUL) // check current dir
Bram Moolenaar071d4272004-06-13 20:20:40 +00001800 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001801 if (fname == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001802 {
1803#ifdef VMS
1804 names[0] = vim_strsave((char_u *)"*_sw%");
1805#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001806 names[0] = vim_strsave((char_u *)"*.sw?");
Bram Moolenaar071d4272004-06-13 20:20:40 +00001807#endif
Bram Moolenaar4f974752019-02-17 17:44:42 +01001808#if defined(UNIX) || defined(MSWIN)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001809 // For Unix names starting with a dot are special. MS-Windows
1810 // supports this too, on some file systems.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001811 names[1] = vim_strsave((char_u *)".*.sw?");
1812 names[2] = vim_strsave((char_u *)".sw?");
1813 num_names = 3;
1814#else
1815# ifdef VMS
1816 names[1] = vim_strsave((char_u *)".*_sw%");
1817 num_names = 2;
1818# else
1819 num_names = 1;
1820# endif
1821#endif
1822 }
1823 else
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001824 num_names = recov_file_names(names, fname_res, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001825 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001826 else // check directory dir_name
Bram Moolenaar071d4272004-06-13 20:20:40 +00001827 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001828 if (fname == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001829 {
1830#ifdef VMS
1831 names[0] = concat_fnames(dir_name, (char_u *)"*_sw%", TRUE);
1832#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001833 names[0] = concat_fnames(dir_name, (char_u *)"*.sw?", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001834#endif
Bram Moolenaar4f974752019-02-17 17:44:42 +01001835#if defined(UNIX) || defined(MSWIN)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001836 // For Unix names starting with a dot are special. MS-Windows
1837 // supports this too, on some file systems.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001838 names[1] = concat_fnames(dir_name, (char_u *)".*.sw?", TRUE);
1839 names[2] = concat_fnames(dir_name, (char_u *)".sw?", TRUE);
1840 num_names = 3;
1841#else
1842# ifdef VMS
1843 names[1] = concat_fnames(dir_name, (char_u *)".*_sw%", TRUE);
1844 num_names = 2;
1845# else
1846 num_names = 1;
1847# endif
1848#endif
1849 }
1850 else
1851 {
Bram Moolenaar4f974752019-02-17 17:44:42 +01001852#if defined(UNIX) || defined(MSWIN)
Bram Moolenaarb113c3a2017-02-28 21:26:17 +01001853 int len = (int)STRLEN(dir_name);
Bram Moolenaarc525e3a2017-02-18 16:59:02 +01001854
1855 p = dir_name + len;
1856 if (after_pathsep(dir_name, p) && len > 1 && p[-1] == p[-2])
Bram Moolenaar071d4272004-06-13 20:20:40 +00001857 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001858 // Ends with '//', Use Full path for swap name
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001859 tail = make_percent_swname(dir_name, fname_res);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001860 }
1861 else
1862#endif
1863 {
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001864 tail = gettail(fname_res);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001865 tail = concat_fnames(dir_name, tail, TRUE);
1866 }
1867 if (tail == NULL)
1868 num_names = 0;
1869 else
1870 {
1871 num_names = recov_file_names(names, tail, FALSE);
1872 vim_free(tail);
1873 }
1874 }
1875 }
1876
Bram Moolenaar0d3cb732019-05-18 13:05:18 +02001877 // check for out-of-memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00001878 for (i = 0; i < num_names; ++i)
1879 {
1880 if (names[i] == NULL)
1881 {
1882 for (i = 0; i < num_names; ++i)
1883 vim_free(names[i]);
1884 num_names = 0;
1885 }
1886 }
1887 if (num_names == 0)
1888 num_files = 0;
1889 else if (expand_wildcards(num_names, names, &num_files, &files,
Bram Moolenaar99499b12019-05-23 21:35:48 +02001890 EW_NOTENV|EW_KEEPALL|EW_FILE|EW_SILENT) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001891 num_files = 0;
1892
1893 /*
1894 * When no swap file found, wildcard expansion might have failed (e.g.
1895 * not able to execute the shell).
1896 * Try finding a swap file by simply adding ".swp" to the file name.
1897 */
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001898 if (*dirp == NUL && file_count + num_files == 0 && fname != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001899 {
Bram Moolenaar8767f522016-07-01 17:17:39 +02001900 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001901 char_u *swapname;
1902
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001903 swapname = modname(fname_res,
Bram Moolenaare60acc12011-05-10 16:41:25 +02001904#if defined(VMS)
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001905 (char_u *)"_swp", FALSE
Bram Moolenaar071d4272004-06-13 20:20:40 +00001906#else
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001907 (char_u *)".swp", TRUE
Bram Moolenaar071d4272004-06-13 20:20:40 +00001908#endif
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02001909 );
Bram Moolenaar071d4272004-06-13 20:20:40 +00001910 if (swapname != NULL)
1911 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001912 if (mch_stat((char *)swapname, &st) != -1) // It exists!
Bram Moolenaar071d4272004-06-13 20:20:40 +00001913 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001914 files = ALLOC_ONE(char_u *);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001915 if (files != NULL)
1916 {
1917 files[0] = swapname;
1918 swapname = NULL;
1919 num_files = 1;
1920 }
1921 }
1922 vim_free(swapname);
1923 }
1924 }
1925
1926 /*
1927 * remove swapfile name of the current buffer, it must be ignored
1928 */
1929 if (curbuf->b_ml.ml_mfp != NULL
1930 && (p = curbuf->b_ml.ml_mfp->mf_fname) != NULL)
1931 {
1932 for (i = 0; i < num_files; ++i)
Bram Moolenaar99499b12019-05-23 21:35:48 +02001933 // Do not expand wildcards, on windows would try to expand
1934 // "%tmp%" in "%tmp%file".
1935 if (fullpathcmp(p, files[i], TRUE, FALSE) & FPC_SAME)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001936 {
Bram Moolenaar99499b12019-05-23 21:35:48 +02001937 // Remove the name from files[i]. Move further entries
1938 // down. When the array becomes empty free it here, since
1939 // FreeWild() won't be called below.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001940 vim_free(files[i]);
Bram Moolenaar9439cdd2009-04-22 13:39:36 +00001941 if (--num_files == 0)
1942 vim_free(files);
1943 else
1944 for ( ; i < num_files; ++i)
1945 files[i] = files[i + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00001946 }
1947 }
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00001948 if (nr > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001949 {
1950 file_count += num_files;
1951 if (nr <= file_count)
1952 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001953 *fname_out = vim_strsave(
1954 files[nr - 1 + num_files - file_count]);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001955 dirp = (char_u *)""; // stop searching
Bram Moolenaar071d4272004-06-13 20:20:40 +00001956 }
1957 }
1958 else if (list)
1959 {
1960 if (dir_name[0] == '.' && dir_name[1] == NUL)
1961 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001962 if (fname == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001963 msg_puts(_(" In current directory:\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001964 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001965 msg_puts(_(" Using specified name:\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001966 }
1967 else
1968 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001969 msg_puts(_(" In directory "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001970 msg_home_replace(dir_name);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001971 msg_puts(":\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00001972 }
1973
1974 if (num_files)
1975 {
1976 for (i = 0; i < num_files; ++i)
1977 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001978 // print the swap file name
Bram Moolenaar071d4272004-06-13 20:20:40 +00001979 msg_outnum((long)++file_count);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001980 msg_puts(". ");
1981 msg_puts((char *)gettail(files[i]));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001982 msg_putchar('\n');
1983 (void)swapfile_info(files[i]);
1984 }
1985 }
1986 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001987 msg_puts(_(" -- none --\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001988 out_flush();
1989 }
1990 else
1991 file_count += num_files;
1992
1993 for (i = 0; i < num_names; ++i)
1994 vim_free(names[i]);
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00001995 if (num_files > 0)
1996 FreeWild(num_files, files);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001997 }
1998 vim_free(dir_name);
1999 return file_count;
2000}
2001
Bram Moolenaar4f974752019-02-17 17:44:42 +01002002#if defined(UNIX) || defined(MSWIN) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002003/*
Bram Moolenaarb782ba42018-08-07 21:39:28 +02002004 * Need _very_ long file names.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002005 * Append the full path to name with path separators made into percent
2006 * signs, to dir. An unnamed buffer is handled as "" (<currentdir>/"")
2007 */
Bram Moolenaarb782ba42018-08-07 21:39:28 +02002008 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002009make_percent_swname(char_u *dir, char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002010{
Bram Moolenaarb782ba42018-08-07 21:39:28 +02002011 char_u *d = NULL, *s, *f;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002012
Bram Moolenaarb782ba42018-08-07 21:39:28 +02002013 f = fix_fname(name != NULL ? name : (char_u *)"");
Bram Moolenaar071d4272004-06-13 20:20:40 +00002014 if (f != NULL)
2015 {
Bram Moolenaar964b3742019-05-24 18:54:09 +02002016 s = alloc(STRLEN(f) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002017 if (s != NULL)
2018 {
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002019 STRCPY(s, f);
Bram Moolenaar91acfff2017-03-12 19:22:36 +01002020 for (d = s; *d != NUL; MB_PTR_ADV(d))
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002021 if (vim_ispathsep(*d))
2022 *d = '%';
Bram Moolenaar071d4272004-06-13 20:20:40 +00002023 d = concat_fnames(dir, s, TRUE);
2024 vim_free(s);
2025 }
2026 vim_free(f);
2027 }
2028 return d;
2029}
2030#endif
2031
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02002032#if (defined(UNIX) || defined(VMS) || defined(MSWIN)) \
2033 && (defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG))
2034# define HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar071d4272004-06-13 20:20:40 +00002035static int process_still_running;
2036#endif
2037
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002038#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002039/*
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002040 * Return information found in swapfile "fname" in dictionary "d".
2041 * This is used by the swapinfo() function.
2042 */
2043 void
2044get_b0_dict(char_u *fname, dict_T *d)
2045{
2046 int fd;
2047 struct block0 b0;
2048
2049 if ((fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0)) >= 0)
2050 {
2051 if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0))
2052 {
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002053 if (ml_check_b0_id(&b0) == FAIL)
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002054 dict_add_string(d, "error", (char_u *)"Not a swap file");
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002055 else if (b0_magic_wrong(&b0))
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002056 dict_add_string(d, "error", (char_u *)"Magic number mismatch");
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002057 else
2058 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002059 // we have swap information
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002060 dict_add_string_len(d, "version", b0.b0_version, 10);
2061 dict_add_string_len(d, "user", b0.b0_uname, B0_UNAME_SIZE);
2062 dict_add_string_len(d, "host", b0.b0_hname, B0_HNAME_SIZE);
2063 dict_add_string_len(d, "fname", b0.b0_fname, B0_FNAME_SIZE_ORG);
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002064
2065 dict_add_number(d, "pid", char_to_long(b0.b0_pid));
2066 dict_add_number(d, "mtime", char_to_long(b0.b0_mtime));
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002067 dict_add_number(d, "dirty", b0.b0_dirty ? 1 : 0);
2068# ifdef CHECK_INODE
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002069 dict_add_number(d, "inode", char_to_long(b0.b0_ino));
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002070# endif
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002071 }
2072 }
2073 else
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002074 dict_add_string(d, "error", (char_u *)"Cannot read file");
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002075 close(fd);
2076 }
2077 else
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002078 dict_add_string(d, "error", (char_u *)"Cannot open file");
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002079}
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002080#endif
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002081
2082/*
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00002083 * Give information about an existing swap file.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002084 * Returns timestamp (0 when unknown).
2085 */
2086 static time_t
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002087swapfile_info(char_u *fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002088{
Bram Moolenaar8767f522016-07-01 17:17:39 +02002089 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002090 int fd;
2091 struct block0 b0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002092#ifdef UNIX
2093 char_u uname[B0_UNAME_SIZE];
2094#endif
2095
Bram Moolenaar63d25552019-05-10 21:28:38 +02002096 // print the swap file date
Bram Moolenaar071d4272004-06-13 20:20:40 +00002097 if (mch_stat((char *)fname, &st) != -1)
2098 {
2099#ifdef UNIX
Bram Moolenaar63d25552019-05-10 21:28:38 +02002100 // print name of owner of the file
Bram Moolenaar071d4272004-06-13 20:20:40 +00002101 if (mch_get_uname(st.st_uid, uname, B0_UNAME_SIZE) == OK)
2102 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002103 msg_puts(_(" owned by: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002104 msg_outtrans(uname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01002105 msg_puts(_(" dated: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002106 }
2107 else
2108#endif
Bram Moolenaar32526b32019-01-19 17:43:09 +01002109 msg_puts(_(" dated: "));
Bram Moolenaar63d25552019-05-10 21:28:38 +02002110 msg_puts(get_ctime(st.st_mtime, TRUE));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002111 }
Bram Moolenaar63d25552019-05-10 21:28:38 +02002112 else
2113 st.st_mtime = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002114
2115 /*
2116 * print the original file name
2117 */
2118 fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
2119 if (fd >= 0)
2120 {
Bram Moolenaar540fc6f2010-12-17 16:27:16 +01002121 if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002122 {
2123 if (STRNCMP(b0.b0_version, "VIM 3.0", 7) == 0)
2124 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002125 msg_puts(_(" [from Vim version 3.0]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002126 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002127 else if (ml_check_b0_id(&b0) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002128 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002129 msg_puts(_(" [does not look like a Vim swap file]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002130 }
2131 else
2132 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002133 msg_puts(_(" file name: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002134 if (b0.b0_fname[0] == NUL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002135 msg_puts(_("[No Name]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002136 else
2137 msg_outtrans(b0.b0_fname);
2138
Bram Moolenaar32526b32019-01-19 17:43:09 +01002139 msg_puts(_("\n modified: "));
2140 msg_puts(b0.b0_dirty ? _("YES") : _("no"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002141
2142 if (*(b0.b0_uname) != NUL)
2143 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002144 msg_puts(_("\n user name: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002145 msg_outtrans(b0.b0_uname);
2146 }
2147
2148 if (*(b0.b0_hname) != NUL)
2149 {
2150 if (*(b0.b0_uname) != NUL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002151 msg_puts(_(" host name: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002152 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002153 msg_puts(_("\n host name: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002154 msg_outtrans(b0.b0_hname);
2155 }
2156
2157 if (char_to_long(b0.b0_pid) != 0L)
2158 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002159 msg_puts(_("\n process ID: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002160 msg_outnum(char_to_long(b0.b0_pid));
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002161#if defined(UNIX) || defined(MSWIN)
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02002162 if (mch_process_running(char_to_long(b0.b0_pid)))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002163 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002164 msg_puts(_(" (STILL RUNNING)"));
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02002165# ifdef HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar071d4272004-06-13 20:20:40 +00002166 process_still_running = TRUE;
2167# endif
2168 }
2169#endif
2170 }
2171
2172 if (b0_magic_wrong(&b0))
2173 {
Bram Moolenaar48e330a2016-02-23 14:53:34 +01002174#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002175 if (STRNCMP(b0.b0_hname, "PC ", 3) == 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002176 msg_puts(_("\n [not usable with this version of Vim]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002177 else
2178#endif
Bram Moolenaar32526b32019-01-19 17:43:09 +01002179 msg_puts(_("\n [not usable on this computer]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002180 }
2181 }
2182 }
2183 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002184 msg_puts(_(" [cannot be read]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002185 close(fd);
2186 }
2187 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002188 msg_puts(_(" [cannot be opened]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002189 msg_putchar('\n');
2190
Bram Moolenaar63d25552019-05-10 21:28:38 +02002191 return st.st_mtime;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002192}
2193
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002194/*
2195 * Return TRUE if the swap file looks OK and there are no changes, thus it can
2196 * be safely deleted.
2197 */
2198 static time_t
2199swapfile_unchanged(char_u *fname)
2200{
2201 stat_T st;
2202 int fd;
2203 struct block0 b0;
2204 int ret = TRUE;
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02002205#if defined(UNIX) || defined(MSWIN)
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002206 long pid;
2207#endif
2208
2209 // must be able to stat the swap file
2210 if (mch_stat((char *)fname, &st) == -1)
2211 return FALSE;
2212
2213 // must be able to read the first block
2214 fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
2215 if (fd < 0)
2216 return FALSE;
2217 if (read_eintr(fd, &b0, sizeof(b0)) != sizeof(b0))
2218 {
2219 close(fd);
2220 return FALSE;
2221 }
2222
2223 // the ID and magic number must be correct
2224 if (ml_check_b0_id(&b0) == FAIL|| b0_magic_wrong(&b0))
2225 ret = FALSE;
2226
2227 // must be unchanged
2228 if (b0.b0_dirty)
2229 ret = FALSE;
2230
2231#if defined(UNIX) || defined(MSWIN)
Bram Moolenaar32aa1022019-11-02 22:54:41 +01002232 // process must be known and not be running
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002233 pid = char_to_long(b0.b0_pid);
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02002234 if (pid == 0L || mch_process_running(pid))
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002235 ret = FALSE;
2236#endif
2237
2238 // TODO: Should we check if the swap file was created on the current
2239 // system? And the current user?
2240
2241 close(fd);
2242 return ret;
2243}
2244
Bram Moolenaar071d4272004-06-13 20:20:40 +00002245 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002246recov_file_names(char_u **names, char_u *path, int prepend_dot)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002247{
2248 int num_names;
2249
Bram Moolenaar071d4272004-06-13 20:20:40 +00002250 /*
2251 * (Win32 and Win64) never short names, but do prepend a dot.
2252 * (Not MS-DOS or Win32 or Win64) maybe short name, maybe not: Try both.
2253 * Only use the short name if it is different.
2254 */
2255 char_u *p;
2256 int i;
Bram Moolenaar4f974752019-02-17 17:44:42 +01002257# ifndef MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002258 int shortname = curbuf->b_shortname;
2259
2260 curbuf->b_shortname = FALSE;
2261# endif
2262
2263 num_names = 0;
2264
2265 /*
2266 * May also add the file name with a dot prepended, for swap file in same
2267 * dir as original file.
2268 */
2269 if (prepend_dot)
2270 {
2271 names[num_names] = modname(path, (char_u *)".sw?", TRUE);
2272 if (names[num_names] == NULL)
2273 goto end;
2274 ++num_names;
2275 }
2276
2277 /*
2278 * Form the normal swap file name pattern by appending ".sw?".
2279 */
2280#ifdef VMS
2281 names[num_names] = concat_fnames(path, (char_u *)"_sw%", FALSE);
2282#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002283 names[num_names] = concat_fnames(path, (char_u *)".sw?", FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002284#endif
2285 if (names[num_names] == NULL)
2286 goto end;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002287 if (num_names >= 1) // check if we have the same name twice
Bram Moolenaar071d4272004-06-13 20:20:40 +00002288 {
2289 p = names[num_names - 1];
2290 i = (int)STRLEN(names[num_names - 1]) - (int)STRLEN(names[num_names]);
2291 if (i > 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002292 p += i; // file name has been expanded to full path
Bram Moolenaar071d4272004-06-13 20:20:40 +00002293
2294 if (STRCMP(p, names[num_names]) != 0)
2295 ++num_names;
2296 else
2297 vim_free(names[num_names]);
2298 }
2299 else
2300 ++num_names;
2301
Bram Moolenaar4f974752019-02-17 17:44:42 +01002302# ifndef MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002303 /*
2304 * Also try with 'shortname' set, in case the file is on a DOS filesystem.
2305 */
2306 curbuf->b_shortname = TRUE;
2307#ifdef VMS
2308 names[num_names] = modname(path, (char_u *)"_sw%", FALSE);
2309#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002310 names[num_names] = modname(path, (char_u *)".sw?", FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002311#endif
2312 if (names[num_names] == NULL)
2313 goto end;
2314
2315 /*
2316 * Remove the one from 'shortname', if it's the same as with 'noshortname'.
2317 */
2318 p = names[num_names];
2319 i = STRLEN(names[num_names]) - STRLEN(names[num_names - 1]);
2320 if (i > 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002321 p += i; // file name has been expanded to full path
Bram Moolenaar071d4272004-06-13 20:20:40 +00002322 if (STRCMP(names[num_names - 1], p) == 0)
2323 vim_free(names[num_names]);
2324 else
2325 ++num_names;
2326# endif
2327
2328end:
Bram Moolenaar4f974752019-02-17 17:44:42 +01002329# ifndef MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002330 curbuf->b_shortname = shortname;
2331# endif
2332
Bram Moolenaar071d4272004-06-13 20:20:40 +00002333 return num_names;
2334}
2335
2336/*
2337 * sync all memlines
2338 *
2339 * If 'check_file' is TRUE, check if original file exists and was not changed.
2340 * If 'check_char' is TRUE, stop syncing when character becomes available, but
2341 * always sync at least one block.
2342 */
2343 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002344ml_sync_all(int check_file, int check_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002345{
2346 buf_T *buf;
Bram Moolenaar8767f522016-07-01 17:17:39 +02002347 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002348
Bram Moolenaar29323592016-07-24 22:04:11 +02002349 FOR_ALL_BUFFERS(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002350 {
2351 if (buf->b_ml.ml_mfp == NULL || buf->b_ml.ml_mfp->mf_fname == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002352 continue; // no file
Bram Moolenaar071d4272004-06-13 20:20:40 +00002353
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002354 ml_flush_line(buf); // flush buffered line
2355 // flush locked block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002356 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH);
2357 if (bufIsChanged(buf) && check_file && mf_need_trans(buf->b_ml.ml_mfp)
2358 && buf->b_ffname != NULL)
2359 {
2360 /*
2361 * If the original file does not exist anymore or has been changed
2362 * call ml_preserve() to get rid of all negative numbered blocks.
2363 */
2364 if (mch_stat((char *)buf->b_ffname, &st) == -1
2365 || st.st_mtime != buf->b_mtime_read
Bram Moolenaar914703b2010-05-31 21:59:46 +02002366 || st.st_size != buf->b_orig_size)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002367 {
2368 ml_preserve(buf, FALSE);
2369 did_check_timestamps = FALSE;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002370 need_check_timestamps = TRUE; // give message later
Bram Moolenaar071d4272004-06-13 20:20:40 +00002371 }
2372 }
2373 if (buf->b_ml.ml_mfp->mf_dirty)
2374 {
2375 (void)mf_sync(buf->b_ml.ml_mfp, (check_char ? MFS_STOP : 0)
2376 | (bufIsChanged(buf) ? MFS_FLUSH : 0));
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002377 if (check_char && ui_char_avail()) // character available now
Bram Moolenaar071d4272004-06-13 20:20:40 +00002378 break;
2379 }
2380 }
2381}
2382
2383/*
2384 * sync one buffer, including negative blocks
2385 *
2386 * after this all the blocks are in the swap file
2387 *
2388 * Used for the :preserve command and when the original file has been
2389 * changed or deleted.
2390 *
2391 * when message is TRUE the success of preserving is reported
2392 */
2393 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002394ml_preserve(buf_T *buf, int message)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002395{
2396 bhdr_T *hp;
2397 linenr_T lnum;
2398 memfile_T *mfp = buf->b_ml.ml_mfp;
2399 int status;
2400 int got_int_save = got_int;
2401
2402 if (mfp == NULL || mfp->mf_fname == NULL)
2403 {
2404 if (message)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002405 emsg(_("E313: Cannot preserve, there is no swap file"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002406 return;
2407 }
2408
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002409 // We only want to stop when interrupted here, not when interrupted
2410 // before.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002411 got_int = FALSE;
2412
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002413 ml_flush_line(buf); // flush buffered line
2414 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush locked block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002415 status = mf_sync(mfp, MFS_ALL | MFS_FLUSH);
2416
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002417 // stack is invalid after mf_sync(.., MFS_ALL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002418 buf->b_ml.ml_stack_top = 0;
2419
2420 /*
2421 * Some of the data blocks may have been changed from negative to
2422 * positive block number. In that case the pointer blocks need to be
2423 * updated.
2424 *
2425 * We don't know in which pointer block the references are, so we visit
2426 * all data blocks until there are no more translations to be done (or
2427 * we hit the end of the file, which can only happen in case a write fails,
2428 * e.g. when file system if full).
2429 * ml_find_line() does the work by translating the negative block numbers
2430 * when getting the first line of each data block.
2431 */
2432 if (mf_need_trans(mfp) && !got_int)
2433 {
2434 lnum = 1;
2435 while (mf_need_trans(mfp) && lnum <= buf->b_ml.ml_line_count)
2436 {
2437 hp = ml_find_line(buf, lnum, ML_FIND);
2438 if (hp == NULL)
2439 {
2440 status = FAIL;
2441 goto theend;
2442 }
2443 CHECK(buf->b_ml.ml_locked_low != lnum, "low != lnum");
2444 lnum = buf->b_ml.ml_locked_high + 1;
2445 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002446 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush locked block
2447 // sync the updated pointer blocks
Bram Moolenaar071d4272004-06-13 20:20:40 +00002448 if (mf_sync(mfp, MFS_ALL | MFS_FLUSH) == FAIL)
2449 status = FAIL;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002450 buf->b_ml.ml_stack_top = 0; // stack is invalid now
Bram Moolenaar071d4272004-06-13 20:20:40 +00002451 }
2452theend:
2453 got_int |= got_int_save;
2454
2455 if (message)
2456 {
2457 if (status == OK)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002458 msg(_("File preserved"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002459 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002460 emsg(_("E314: Preserve failed"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002461 }
2462}
2463
2464/*
2465 * NOTE: The pointer returned by the ml_get_*() functions only remains valid
2466 * until the next call!
2467 * line1 = ml_get(1);
2468 * line2 = ml_get(2); // line1 is now invalid!
2469 * Make a copy of the line if necessary.
2470 */
2471/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002472 * Return a pointer to a (read-only copy of a) line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002473 *
2474 * On failure an error message is given and IObuff is returned (to avoid
2475 * having to check for error everywhere).
2476 */
2477 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002478ml_get(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002479{
2480 return ml_get_buf(curbuf, lnum, FALSE);
2481}
2482
2483/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002484 * Return pointer to position "pos".
Bram Moolenaar071d4272004-06-13 20:20:40 +00002485 */
2486 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002487ml_get_pos(pos_T *pos)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002488{
2489 return (ml_get_buf(curbuf, pos->lnum, FALSE) + pos->col);
2490}
2491
2492/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002493 * Return pointer to cursor line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002494 */
2495 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002496ml_get_curline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002497{
2498 return ml_get_buf(curbuf, curwin->w_cursor.lnum, FALSE);
2499}
2500
2501/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002502 * Return pointer to cursor position.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002503 */
2504 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002505ml_get_cursor(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002506{
2507 return (ml_get_buf(curbuf, curwin->w_cursor.lnum, FALSE) +
2508 curwin->w_cursor.col);
2509}
2510
2511/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002512 * Return a pointer to a line in a specific buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +00002513 *
2514 * "will_change": if TRUE mark the buffer dirty (chars in the line will be
2515 * changed)
2516 */
2517 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002518ml_get_buf(
2519 buf_T *buf,
2520 linenr_T lnum,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002521 int will_change) // line will be changed
Bram Moolenaar071d4272004-06-13 20:20:40 +00002522{
Bram Moolenaarad40f022007-02-13 03:01:39 +00002523 bhdr_T *hp;
2524 DATA_BL *dp;
Bram Moolenaarad40f022007-02-13 03:01:39 +00002525 static int recursive = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002526
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002527 if (lnum > buf->b_ml.ml_line_count) // invalid line number
Bram Moolenaar071d4272004-06-13 20:20:40 +00002528 {
Bram Moolenaarad40f022007-02-13 03:01:39 +00002529 if (recursive == 0)
2530 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002531 // Avoid giving this message for a recursive call, may happen when
2532 // the GUI redraws part of the text.
Bram Moolenaarad40f022007-02-13 03:01:39 +00002533 ++recursive;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002534 siemsg(_("E315: ml_get: invalid lnum: %ld"), lnum);
Bram Moolenaarad40f022007-02-13 03:01:39 +00002535 --recursive;
2536 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002537errorret:
2538 STRCPY(IObuff, "???");
Bram Moolenaaradfde112019-05-25 22:11:45 +02002539 buf->b_ml.ml_line_len = 4;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002540 return IObuff;
2541 }
Bram Moolenaaradfde112019-05-25 22:11:45 +02002542 if (lnum <= 0) // pretend line 0 is line 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00002543 lnum = 1;
2544
Bram Moolenaaradfde112019-05-25 22:11:45 +02002545 if (buf->b_ml.ml_mfp == NULL) // there are no lines
2546 {
2547 buf->b_ml.ml_line_len = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002548 return (char_u *)"";
Bram Moolenaaradfde112019-05-25 22:11:45 +02002549 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002550
Bram Moolenaar37d619f2010-03-10 14:46:26 +01002551 /*
2552 * See if it is the same line as requested last time.
2553 * Otherwise may need to flush last used line.
2554 * Don't use the last used line when 'swapfile' is reset, need to load all
2555 * blocks.
2556 */
Bram Moolenaar47b8b152007-02-07 02:41:57 +00002557 if (buf->b_ml.ml_line_lnum != lnum || mf_dont_release)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002558 {
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002559 unsigned start, end;
2560 colnr_T len;
2561 int idx;
2562
Bram Moolenaar071d4272004-06-13 20:20:40 +00002563 ml_flush_line(buf);
2564
2565 /*
2566 * Find the data block containing the line.
2567 * This also fills the stack with the blocks from the root to the data
2568 * block and releases any locked block.
2569 */
2570 if ((hp = ml_find_line(buf, lnum, ML_FIND)) == NULL)
2571 {
Bram Moolenaarad40f022007-02-13 03:01:39 +00002572 if (recursive == 0)
2573 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002574 // Avoid giving this message for a recursive call, may happen
2575 // when the GUI redraws part of the text.
Bram Moolenaarad40f022007-02-13 03:01:39 +00002576 ++recursive;
Bram Moolenaarcb868932019-10-26 20:56:21 +02002577 get_trans_bufname(buf);
2578 shorten_dir(NameBuff);
2579 siemsg(_("E316: ml_get: cannot find line %ld in buffer %d %s"),
2580 lnum, buf->b_fnum, NameBuff);
Bram Moolenaarad40f022007-02-13 03:01:39 +00002581 --recursive;
2582 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002583 goto errorret;
2584 }
2585
2586 dp = (DATA_BL *)(hp->bh_data);
2587
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002588 idx = lnum - buf->b_ml.ml_locked_low;
2589 start = ((dp->db_index[idx]) & DB_INDEX_MASK);
2590 // The text ends where the previous line starts. The first line ends
2591 // at the end of the block.
2592 if (idx == 0)
2593 end = dp->db_txt_end;
2594 else
2595 end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK);
2596 len = end - start;
2597
2598 buf->b_ml.ml_line_ptr = (char_u *)dp + start;
2599 buf->b_ml.ml_line_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002600 buf->b_ml.ml_line_lnum = lnum;
2601 buf->b_ml.ml_flags &= ~ML_LINE_DIRTY;
2602 }
2603 if (will_change)
2604 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
2605
2606 return buf->b_ml.ml_line_ptr;
2607}
2608
2609/*
2610 * Check if a line that was just obtained by a call to ml_get
2611 * is in allocated memory.
2612 */
2613 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002614ml_line_alloced(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002615{
2616 return (curbuf->b_ml.ml_flags & ML_LINE_DIRTY);
2617}
2618
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002619#ifdef FEAT_PROP_POPUP
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002620/*
2621 * Add text properties that continue from the previous line.
2622 */
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002623 static void
2624add_text_props_for_append(
2625 buf_T *buf,
2626 linenr_T lnum,
2627 char_u **line,
2628 int *len,
2629 char_u **tofree)
2630{
2631 int round;
2632 int new_prop_count = 0;
2633 int count;
2634 int n;
2635 char_u *props;
Bram Moolenaarea781452019-09-04 18:53:12 +02002636 int new_len = 0; // init for gcc
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002637 char_u *new_line;
2638 textprop_T prop;
2639
2640 // Make two rounds:
2641 // 1. calculate the extra space needed
2642 // 2. allocate the space and fill it
2643 for (round = 1; round <= 2; ++round)
2644 {
2645 if (round == 2)
2646 {
2647 if (new_prop_count == 0)
2648 return; // nothing to do
2649 new_len = *len + new_prop_count * sizeof(textprop_T);
Bram Moolenaar964b3742019-05-24 18:54:09 +02002650 new_line = alloc(new_len);
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002651 if (new_line == NULL)
2652 return;
2653 mch_memmove(new_line, *line, *len);
2654 new_prop_count = 0;
2655 }
2656
2657 // Get the line above to find any props that continue in the next
2658 // line.
2659 count = get_text_props(buf, lnum, &props, FALSE);
2660 for (n = 0; n < count; ++n)
2661 {
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002662 mch_memmove(&prop, props + n * sizeof(textprop_T),
2663 sizeof(textprop_T));
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002664 if (prop.tp_flags & TP_FLAG_CONT_NEXT)
2665 {
2666 if (round == 2)
2667 {
2668 prop.tp_flags |= TP_FLAG_CONT_PREV;
2669 prop.tp_col = 1;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002670 prop.tp_len = *len; // not exactly the right length
2671 mch_memmove(new_line + *len + new_prop_count
2672 * sizeof(textprop_T), &prop, sizeof(textprop_T));
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002673 }
2674 ++new_prop_count;
2675 }
2676 }
2677 }
2678 *line = new_line;
2679 *tofree = new_line;
2680 *len = new_len;
2681}
2682#endif
2683
Bram Moolenaar071d4272004-06-13 20:20:40 +00002684 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002685ml_append_int(
2686 buf_T *buf,
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002687 linenr_T lnum, // append after this line (can be 0)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002688 char_u *line_arg, // text of the new line
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002689 colnr_T len_arg, // length of line, including NUL, or 0
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002690 int flags) // ML_APPEND_ flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00002691{
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002692 char_u *line = line_arg;
2693 colnr_T len = len_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002694 int i;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002695 int line_count; // number of indexes in current block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002696 int offset;
2697 int from, to;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002698 int space_needed; // space needed for new line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002699 int page_size;
2700 int page_count;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002701 int db_idx; // index for lnum in data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002702 bhdr_T *hp;
2703 memfile_T *mfp;
2704 DATA_BL *dp;
2705 PTR_BL *pp;
2706 infoptr_T *ip;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002707#ifdef FEAT_PROP_POPUP
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002708 char_u *tofree = NULL;
2709#endif
2710 int ret = FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002711
Bram Moolenaar071d4272004-06-13 20:20:40 +00002712 if (lnum > buf->b_ml.ml_line_count || buf->b_ml.ml_mfp == NULL)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002713 return FAIL; // lnum out of range
Bram Moolenaar071d4272004-06-13 20:20:40 +00002714
2715 if (lowest_marked && lowest_marked > lnum)
2716 lowest_marked = lnum + 1;
2717
2718 if (len == 0)
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002719 len = (colnr_T)STRLEN(line) + 1; // space needed for the text
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002720
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002721#ifdef FEAT_PROP_POPUP
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002722 if (curbuf->b_has_textprop && lnum > 0 && !(flags & ML_APPEND_UNDO))
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002723 // Add text properties that continue from the previous line.
2724 add_text_props_for_append(buf, lnum, &line, &len, &tofree);
2725#endif
2726
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002727 space_needed = len + INDEX_SIZE; // space needed for text + index
Bram Moolenaar071d4272004-06-13 20:20:40 +00002728
2729 mfp = buf->b_ml.ml_mfp;
2730 page_size = mfp->mf_page_size;
2731
2732/*
2733 * find the data block containing the previous line
2734 * This also fills the stack with the blocks from the root to the data block
2735 * This also releases any locked block.
2736 */
2737 if ((hp = ml_find_line(buf, lnum == 0 ? (linenr_T)1 : lnum,
2738 ML_INSERT)) == NULL)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002739 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002740
2741 buf->b_ml.ml_flags &= ~ML_EMPTY;
2742
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002743 if (lnum == 0) // got line one instead, correct db_idx
2744 db_idx = -1; // careful, it is negative!
Bram Moolenaar071d4272004-06-13 20:20:40 +00002745 else
2746 db_idx = lnum - buf->b_ml.ml_locked_low;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002747 // get line count before the insertion
Bram Moolenaar071d4272004-06-13 20:20:40 +00002748 line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low;
2749
2750 dp = (DATA_BL *)(hp->bh_data);
2751
2752/*
2753 * If
2754 * - there is not enough room in the current block
2755 * - appending to the last line in the block
2756 * - not appending to the last line in the file
2757 * insert in front of the next block.
2758 */
2759 if ((int)dp->db_free < space_needed && db_idx == line_count - 1
2760 && lnum < buf->b_ml.ml_line_count)
2761 {
2762 /*
2763 * Now that the line is not going to be inserted in the block that we
2764 * expected, the line count has to be adjusted in the pointer blocks
2765 * by using ml_locked_lineadd.
2766 */
2767 --(buf->b_ml.ml_locked_lineadd);
2768 --(buf->b_ml.ml_locked_high);
2769 if ((hp = ml_find_line(buf, lnum + 1, ML_INSERT)) == NULL)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002770 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002771
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002772 db_idx = -1; // careful, it is negative!
2773 // get line count before the insertion
Bram Moolenaar071d4272004-06-13 20:20:40 +00002774 line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low;
2775 CHECK(buf->b_ml.ml_locked_low != lnum + 1, "locked_low != lnum + 1");
2776
2777 dp = (DATA_BL *)(hp->bh_data);
2778 }
2779
2780 ++buf->b_ml.ml_line_count;
2781
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002782 if ((int)dp->db_free >= space_needed) // enough room in data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002783 {
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002784 /*
2785 * Insert the new line in an existing data block, or in the data block
2786 * allocated above.
2787 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002788 dp->db_txt_start -= len;
2789 dp->db_free -= space_needed;
2790 ++(dp->db_line_count);
2791
2792 /*
2793 * move the text of the lines that follow to the front
2794 * adjust the indexes of the lines that follow
2795 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002796 if (line_count > db_idx + 1) // if there are following lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00002797 {
2798 /*
2799 * Offset is the start of the previous line.
2800 * This will become the character just after the new line.
2801 */
2802 if (db_idx < 0)
2803 offset = dp->db_txt_end;
2804 else
2805 offset = ((dp->db_index[db_idx]) & DB_INDEX_MASK);
2806 mch_memmove((char *)dp + dp->db_txt_start,
2807 (char *)dp + dp->db_txt_start + len,
2808 (size_t)(offset - (dp->db_txt_start + len)));
2809 for (i = line_count - 1; i > db_idx; --i)
2810 dp->db_index[i + 1] = dp->db_index[i] - len;
2811 dp->db_index[db_idx + 1] = offset - len;
2812 }
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002813 else
2814 // add line at the end (which is the start of the text)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002815 dp->db_index[db_idx + 1] = dp->db_txt_start;
2816
2817 /*
2818 * copy the text into the block
2819 */
2820 mch_memmove((char *)dp + dp->db_index[db_idx + 1], line, (size_t)len);
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002821 if (flags & ML_APPEND_MARK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002822 dp->db_index[db_idx + 1] |= DB_MARKED;
2823
2824 /*
2825 * Mark the block dirty.
2826 */
2827 buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002828 if (!(flags & ML_APPEND_NEW))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002829 buf->b_ml.ml_flags |= ML_LOCKED_POS;
2830 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002831 else // not enough space in data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002832 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002833 long line_count_left, line_count_right;
2834 int page_count_left, page_count_right;
2835 bhdr_T *hp_left;
2836 bhdr_T *hp_right;
2837 bhdr_T *hp_new;
2838 int lines_moved;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002839 int data_moved = 0; // init to shut up gcc
2840 int total_moved = 0; // init to shut up gcc
Bram Moolenaar071d4272004-06-13 20:20:40 +00002841 DATA_BL *dp_right, *dp_left;
2842 int stack_idx;
2843 int in_left;
2844 int lineadd;
2845 blocknr_T bnum_left, bnum_right;
2846 linenr_T lnum_left, lnum_right;
2847 int pb_idx;
2848 PTR_BL *pp_new;
2849
2850 /*
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002851 * There is not enough room, we have to create a new data block and
2852 * copy some lines into it.
2853 * Then we have to insert an entry in the pointer block.
2854 * If this pointer block also is full, we go up another block, and so
2855 * on, up to the root if necessary.
2856 * The line counts in the pointer blocks have already been adjusted by
2857 * ml_find_line().
2858 *
Bram Moolenaar071d4272004-06-13 20:20:40 +00002859 * We are going to allocate a new data block. Depending on the
2860 * situation it will be put to the left or right of the existing
2861 * block. If possible we put the new line in the left block and move
2862 * the lines after it to the right block. Otherwise the new line is
2863 * also put in the right block. This method is more efficient when
2864 * inserting a lot of lines at one place.
2865 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002866 if (db_idx < 0) // left block is new, right block is existing
Bram Moolenaar071d4272004-06-13 20:20:40 +00002867 {
2868 lines_moved = 0;
2869 in_left = TRUE;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002870 // space_needed does not change
Bram Moolenaar071d4272004-06-13 20:20:40 +00002871 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002872 else // left block is existing, right block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00002873 {
2874 lines_moved = line_count - db_idx - 1;
2875 if (lines_moved == 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002876 in_left = FALSE; // put new line in right block
2877 // space_needed does not change
Bram Moolenaar071d4272004-06-13 20:20:40 +00002878 else
2879 {
2880 data_moved = ((dp->db_index[db_idx]) & DB_INDEX_MASK) -
2881 dp->db_txt_start;
2882 total_moved = data_moved + lines_moved * INDEX_SIZE;
2883 if ((int)dp->db_free + total_moved >= space_needed)
2884 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002885 in_left = TRUE; // put new line in left block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002886 space_needed = total_moved;
2887 }
2888 else
2889 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002890 in_left = FALSE; // put new line in right block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002891 space_needed += total_moved;
2892 }
2893 }
2894 }
2895
2896 page_count = ((space_needed + HEADER_SIZE) + page_size - 1) / page_size;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002897 if ((hp_new = ml_new_data(mfp, flags & ML_APPEND_NEW, page_count))
2898 == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002899 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002900 // correct line counts in pointer blocks
Bram Moolenaar071d4272004-06-13 20:20:40 +00002901 --(buf->b_ml.ml_locked_lineadd);
2902 --(buf->b_ml.ml_locked_high);
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002903 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002904 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002905 if (db_idx < 0) // left block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00002906 {
2907 hp_left = hp_new;
2908 hp_right = hp;
2909 line_count_left = 0;
2910 line_count_right = line_count;
2911 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002912 else // right block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00002913 {
2914 hp_left = hp;
2915 hp_right = hp_new;
2916 line_count_left = line_count;
2917 line_count_right = 0;
2918 }
2919 dp_right = (DATA_BL *)(hp_right->bh_data);
2920 dp_left = (DATA_BL *)(hp_left->bh_data);
2921 bnum_left = hp_left->bh_bnum;
2922 bnum_right = hp_right->bh_bnum;
2923 page_count_left = hp_left->bh_page_count;
2924 page_count_right = hp_right->bh_page_count;
2925
2926 /*
2927 * May move the new line into the right/new block.
2928 */
2929 if (!in_left)
2930 {
2931 dp_right->db_txt_start -= len;
2932 dp_right->db_free -= len + INDEX_SIZE;
2933 dp_right->db_index[0] = dp_right->db_txt_start;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002934 if (flags & ML_APPEND_MARK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002935 dp_right->db_index[0] |= DB_MARKED;
2936
2937 mch_memmove((char *)dp_right + dp_right->db_txt_start,
2938 line, (size_t)len);
2939 ++line_count_right;
2940 }
2941 /*
2942 * may move lines from the left/old block to the right/new one.
2943 */
2944 if (lines_moved)
2945 {
2946 /*
2947 */
2948 dp_right->db_txt_start -= data_moved;
2949 dp_right->db_free -= total_moved;
2950 mch_memmove((char *)dp_right + dp_right->db_txt_start,
2951 (char *)dp_left + dp_left->db_txt_start,
2952 (size_t)data_moved);
2953 offset = dp_right->db_txt_start - dp_left->db_txt_start;
2954 dp_left->db_txt_start += data_moved;
2955 dp_left->db_free += total_moved;
2956
2957 /*
2958 * update indexes in the new block
2959 */
2960 for (to = line_count_right, from = db_idx + 1;
2961 from < line_count_left; ++from, ++to)
2962 dp_right->db_index[to] = dp->db_index[from] + offset;
2963 line_count_right += lines_moved;
2964 line_count_left -= lines_moved;
2965 }
2966
2967 /*
2968 * May move the new line into the left (old or new) block.
2969 */
2970 if (in_left)
2971 {
2972 dp_left->db_txt_start -= len;
2973 dp_left->db_free -= len + INDEX_SIZE;
2974 dp_left->db_index[line_count_left] = dp_left->db_txt_start;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02002975 if (flags & ML_APPEND_MARK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002976 dp_left->db_index[line_count_left] |= DB_MARKED;
2977 mch_memmove((char *)dp_left + dp_left->db_txt_start,
2978 line, (size_t)len);
2979 ++line_count_left;
2980 }
2981
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 lnum_left = lnum + 1;
2985 lnum_right = 0;
2986 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01002987 else // right block is new
Bram Moolenaar071d4272004-06-13 20:20:40 +00002988 {
2989 lnum_left = 0;
2990 if (in_left)
2991 lnum_right = lnum + 2;
2992 else
2993 lnum_right = lnum + 1;
2994 }
2995 dp_left->db_line_count = line_count_left;
2996 dp_right->db_line_count = line_count_right;
2997
2998 /*
2999 * release the two data blocks
3000 * The new one (hp_new) already has a correct blocknumber.
3001 * The old one (hp, in ml_locked) gets a positive blocknumber if
3002 * we changed it and we are not editing a new file.
3003 */
3004 if (lines_moved || in_left)
3005 buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003006 if (!(flags & ML_APPEND_NEW) && db_idx >= 0 && in_left)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003007 buf->b_ml.ml_flags |= ML_LOCKED_POS;
3008 mf_put(mfp, hp_new, TRUE, FALSE);
3009
3010 /*
3011 * flush the old data block
3012 * set ml_locked_lineadd to 0, because the updating of the
3013 * pointer blocks is done below
3014 */
3015 lineadd = buf->b_ml.ml_locked_lineadd;
3016 buf->b_ml.ml_locked_lineadd = 0;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003017 ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003018
3019 /*
3020 * update pointer blocks for the new data block
3021 */
3022 for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0;
3023 --stack_idx)
3024 {
3025 ip = &(buf->b_ml.ml_stack[stack_idx]);
3026 pb_idx = ip->ip_index;
3027 if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003028 goto theend;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003029 pp = (PTR_BL *)(hp->bh_data); // must be pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003030 if (pp->pb_id != PTR_ID)
3031 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003032 iemsg(_("E317: pointer block id wrong 3"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003033 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003034 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003035 }
3036 /*
3037 * TODO: If the pointer block is full and we are adding at the end
3038 * try to insert in front of the next block
3039 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003040 // block not full, add one entry
Bram Moolenaar071d4272004-06-13 20:20:40 +00003041 if (pp->pb_count < pp->pb_count_max)
3042 {
3043 if (pb_idx + 1 < (int)pp->pb_count)
3044 mch_memmove(&pp->pb_pointer[pb_idx + 2],
3045 &pp->pb_pointer[pb_idx + 1],
3046 (size_t)(pp->pb_count - pb_idx - 1) * sizeof(PTR_EN));
3047 ++pp->pb_count;
3048 pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
3049 pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
3050 pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
3051 pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right;
3052 pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right;
3053 pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right;
3054
3055 if (lnum_left != 0)
3056 pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left;
3057 if (lnum_right != 0)
3058 pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right;
3059
3060 mf_put(mfp, hp, TRUE, FALSE);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003061 buf->b_ml.ml_stack_top = stack_idx + 1; // truncate stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00003062
3063 if (lineadd)
3064 {
3065 --(buf->b_ml.ml_stack_top);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003066 // fix line count for rest of blocks in the stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00003067 ml_lineadd(buf, lineadd);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003068 // fix stack itself
Bram Moolenaar071d4272004-06-13 20:20:40 +00003069 buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high +=
3070 lineadd;
3071 ++(buf->b_ml.ml_stack_top);
3072 }
3073
3074 /*
3075 * We are finished, break the loop here.
3076 */
3077 break;
3078 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003079 else // pointer block full
Bram Moolenaar071d4272004-06-13 20:20:40 +00003080 {
3081 /*
3082 * split the pointer block
3083 * allocate a new pointer block
3084 * move some of the pointer into the new block
3085 * prepare for updating the parent block
3086 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003087 for (;;) // do this twice when splitting block 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00003088 {
3089 hp_new = ml_new_ptr(mfp);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003090 if (hp_new == NULL) // TODO: try to fix tree
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003091 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003092 pp_new = (PTR_BL *)(hp_new->bh_data);
3093
3094 if (hp->bh_bnum != 1)
3095 break;
3096
3097 /*
3098 * if block 1 becomes full the tree is given an extra level
3099 * The pointers from block 1 are moved into the new block.
3100 * block 1 is updated to point to the new block
3101 * then continue to split the new block
3102 */
3103 mch_memmove(pp_new, pp, (size_t)page_size);
3104 pp->pb_count = 1;
3105 pp->pb_pointer[0].pe_bnum = hp_new->bh_bnum;
3106 pp->pb_pointer[0].pe_line_count = buf->b_ml.ml_line_count;
3107 pp->pb_pointer[0].pe_old_lnum = 1;
3108 pp->pb_pointer[0].pe_page_count = 1;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003109 mf_put(mfp, hp, TRUE, FALSE); // release block 1
3110 hp = hp_new; // new block is to be split
Bram Moolenaar071d4272004-06-13 20:20:40 +00003111 pp = pp_new;
3112 CHECK(stack_idx != 0, _("stack_idx should be 0"));
3113 ip->ip_index = 0;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003114 ++stack_idx; // do block 1 again later
Bram Moolenaar071d4272004-06-13 20:20:40 +00003115 }
3116 /*
3117 * move the pointers after the current one to the new block
3118 * If there are none, the new entry will be in the new block.
3119 */
3120 total_moved = pp->pb_count - pb_idx - 1;
3121 if (total_moved)
3122 {
3123 mch_memmove(&pp_new->pb_pointer[0],
3124 &pp->pb_pointer[pb_idx + 1],
3125 (size_t)(total_moved) * sizeof(PTR_EN));
3126 pp_new->pb_count = total_moved;
3127 pp->pb_count -= total_moved - 1;
3128 pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right;
3129 pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right;
3130 pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right;
3131 if (lnum_right)
3132 pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right;
3133 }
3134 else
3135 {
3136 pp_new->pb_count = 1;
3137 pp_new->pb_pointer[0].pe_bnum = bnum_right;
3138 pp_new->pb_pointer[0].pe_line_count = line_count_right;
3139 pp_new->pb_pointer[0].pe_page_count = page_count_right;
3140 pp_new->pb_pointer[0].pe_old_lnum = lnum_right;
3141 }
3142 pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
3143 pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
3144 pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
3145 if (lnum_left)
3146 pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left;
3147 lnum_left = 0;
3148 lnum_right = 0;
3149
3150 /*
3151 * recompute line counts
3152 */
3153 line_count_right = 0;
3154 for (i = 0; i < (int)pp_new->pb_count; ++i)
3155 line_count_right += pp_new->pb_pointer[i].pe_line_count;
3156 line_count_left = 0;
3157 for (i = 0; i < (int)pp->pb_count; ++i)
3158 line_count_left += pp->pb_pointer[i].pe_line_count;
3159
3160 bnum_left = hp->bh_bnum;
3161 bnum_right = hp_new->bh_bnum;
3162 page_count_left = 1;
3163 page_count_right = 1;
3164 mf_put(mfp, hp, TRUE, FALSE);
3165 mf_put(mfp, hp_new, TRUE, FALSE);
3166 }
3167 }
3168
3169 /*
3170 * Safety check: fallen out of for loop?
3171 */
3172 if (stack_idx < 0)
3173 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003174 iemsg(_("E318: Updated too many blocks?"));
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003175 buf->b_ml.ml_stack_top = 0; // invalidate stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00003176 }
3177 }
3178
3179#ifdef FEAT_BYTEOFF
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003180 // The line was inserted below 'lnum'
Bram Moolenaar071d4272004-06-13 20:20:40 +00003181 ml_updatechunk(buf, lnum + 1, (long)len, ML_CHNK_ADDLINE);
3182#endif
3183#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02003184 if (netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +00003185 {
3186 if (STRLEN(line) > 0)
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003187 netbeans_inserted(buf, lnum+1, (colnr_T)0, line, (int)STRLEN(line));
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00003188 netbeans_inserted(buf, lnum+1, (colnr_T)STRLEN(line),
Bram Moolenaar071d4272004-06-13 20:20:40 +00003189 (char_u *)"\n", 1);
3190 }
3191#endif
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01003192#ifdef FEAT_JOB_CHANNEL
Bram Moolenaar99ef0622016-03-06 20:22:25 +01003193 if (buf->b_write_to_channel)
3194 channel_write_new_lines(buf);
3195#endif
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003196 ret = OK;
Bram Moolenaar99ef0622016-03-06 20:22:25 +01003197
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003198theend:
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003199#ifdef FEAT_PROP_POPUP
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003200 vim_free(tofree);
3201#endif
3202 return ret;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003203}
3204
3205/*
Bram Moolenaar250e3112019-07-15 23:02:14 +02003206 * Flush any pending change and call ml_append_int()
3207 */
3208 static int
3209ml_append_flush(
3210 buf_T *buf,
3211 linenr_T lnum, // append after this line (can be 0)
3212 char_u *line, // text of the new line
3213 colnr_T len, // length of line, including NUL, or 0
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003214 int flags) // ML_APPEND_ flags
Bram Moolenaar250e3112019-07-15 23:02:14 +02003215{
3216 if (lnum > buf->b_ml.ml_line_count)
3217 return FAIL; // lnum out of range
3218
3219 if (buf->b_ml.ml_line_lnum != 0)
3220 // This may also invoke ml_append_int().
3221 ml_flush_line(buf);
3222
3223#ifdef FEAT_EVAL
3224 // When inserting above recorded changes: flush the changes before changing
3225 // the text. Then flush the cached line, it may become invalid.
3226 may_invoke_listeners(buf, lnum + 1, lnum + 1, 1);
3227 if (buf->b_ml.ml_line_lnum != 0)
3228 ml_flush_line(buf);
3229#endif
3230
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003231 return ml_append_int(buf, lnum, line, len, flags);
Bram Moolenaar250e3112019-07-15 23:02:14 +02003232}
3233
3234/*
3235 * Append a line after lnum (may be 0 to insert a line in front of the file).
3236 * "line" does not need to be allocated, but can't be another line in a
3237 * buffer, unlocking may make it invalid.
3238 *
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003239 * "newfile": TRUE when starting to edit a new file, meaning that pe_old_lnum
Bram Moolenaar250e3112019-07-15 23:02:14 +02003240 * will be set for recovery
3241 * Check: The caller of this function should probably also call
3242 * appended_lines().
3243 *
3244 * return FAIL for failure, OK otherwise
3245 */
3246 int
3247ml_append(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003248 linenr_T lnum, // append after this line (can be 0)
3249 char_u *line, // text of the new line
3250 colnr_T len, // length of new line, including NUL, or 0
3251 int newfile) // flag, see above
Bram Moolenaar250e3112019-07-15 23:02:14 +02003252{
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003253 return ml_append_flags(lnum, line, len, newfile ? ML_APPEND_NEW : 0);
3254}
3255
3256 int
3257ml_append_flags(
3258 linenr_T lnum, // append after this line (can be 0)
3259 char_u *line, // text of the new line
3260 colnr_T len, // length of new line, including NUL, or 0
3261 int flags) // ML_APPEND_ values
3262{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003263 // When starting up, we might still need to create the memfile
Bram Moolenaar250e3112019-07-15 23:02:14 +02003264 if (curbuf->b_ml.ml_mfp == NULL && open_buffer(FALSE, NULL, 0) == FAIL)
3265 return FAIL;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003266 return ml_append_flush(curbuf, lnum, line, len, flags);
Bram Moolenaar250e3112019-07-15 23:02:14 +02003267}
3268
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003269
Bram Moolenaar250e3112019-07-15 23:02:14 +02003270#if defined(FEAT_SPELL) || defined(FEAT_QUICKFIX) || defined(PROTO)
3271/*
3272 * Like ml_append() but for an arbitrary buffer. The buffer must already have
3273 * a memline.
3274 */
3275 int
3276ml_append_buf(
3277 buf_T *buf,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003278 linenr_T lnum, // append after this line (can be 0)
3279 char_u *line, // text of the new line
3280 colnr_T len, // length of new line, including NUL, or 0
3281 int newfile) // flag, see above
Bram Moolenaar250e3112019-07-15 23:02:14 +02003282{
3283 if (buf->b_ml.ml_mfp == NULL)
3284 return FAIL;
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003285 return ml_append_flush(buf, lnum, line, len, newfile ? ML_APPEND_NEW : 0);
Bram Moolenaar250e3112019-07-15 23:02:14 +02003286}
3287#endif
3288
3289/*
Bram Moolenaar5966ea12020-07-15 15:30:05 +02003290 * Replace line "lnum", with buffering, in current buffer.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003291 *
Bram Moolenaar1056d982006-03-09 22:37:52 +00003292 * If "copy" is TRUE, make a copy of the line, otherwise the line has been
Bram Moolenaar071d4272004-06-13 20:20:40 +00003293 * copied to allocated memory already.
Bram Moolenaar5966ea12020-07-15 15:30:05 +02003294 * If "copy" is FALSE the "line" may be freed to add text properties!
3295 * Do not use it after calling ml_replace().
Bram Moolenaar071d4272004-06-13 20:20:40 +00003296 *
3297 * Check: The caller of this function should probably also call
3298 * changed_lines(), unless update_screen(NOT_VALID) is used.
3299 *
3300 * return FAIL for failure, OK otherwise
3301 */
3302 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003303ml_replace(linenr_T lnum, char_u *line, int copy)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003304{
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003305 colnr_T len = -1;
3306
3307 if (line != NULL)
Bram Moolenaar4efe73b2018-12-16 14:37:39 +01003308 len = (colnr_T)STRLEN(line);
Bram Moolenaarccae4672019-01-04 15:09:57 +01003309 return ml_replace_len(lnum, line, len, FALSE, copy);
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003310}
3311
Bram Moolenaarccae4672019-01-04 15:09:57 +01003312/*
3313 * Replace a line for the current buffer. Like ml_replace() with:
3314 * "len_arg" is the length of the text, excluding NUL.
3315 * If "has_props" is TRUE then "line_arg" includes the text properties and
3316 * "len_arg" includes the NUL of the text.
3317 */
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003318 int
Bram Moolenaarccae4672019-01-04 15:09:57 +01003319ml_replace_len(
3320 linenr_T lnum,
3321 char_u *line_arg,
3322 colnr_T len_arg,
3323 int has_props,
3324 int copy)
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003325{
3326 char_u *line = line_arg;
3327 colnr_T len = len_arg;
3328
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003329 if (line == NULL) // just checking...
Bram Moolenaar071d4272004-06-13 20:20:40 +00003330 return FAIL;
3331
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003332 // When starting up, we might still need to create the memfile
Bram Moolenaar59f931e2010-07-24 20:27:03 +02003333 if (curbuf->b_ml.ml_mfp == NULL && open_buffer(FALSE, NULL, 0) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003334 return FAIL;
3335
Bram Moolenaarccae4672019-01-04 15:09:57 +01003336 if (!has_props)
3337 ++len; // include the NUL after the text
3338 if (copy)
3339 {
3340 // copy the line to allocated memory
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003341#ifdef FEAT_PROP_POPUP
Bram Moolenaarccae4672019-01-04 15:09:57 +01003342 if (has_props)
3343 line = vim_memsave(line, len);
3344 else
3345#endif
3346 line = vim_strnsave(line, len - 1);
3347 if (line == NULL)
3348 return FAIL;
3349 }
3350
Bram Moolenaar071d4272004-06-13 20:20:40 +00003351#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02003352 if (netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +00003353 {
3354 netbeans_removed(curbuf, lnum, 0, (long)STRLEN(ml_get(lnum)));
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003355 netbeans_inserted(curbuf, lnum, 0, line, (int)STRLEN(line));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003356 }
3357#endif
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003358 if (curbuf->b_ml.ml_line_lnum != lnum)
3359 {
3360 // another line is buffered, flush it
3361 ml_flush_line(curbuf);
Bram Moolenaarca79a5f2018-12-13 23:16:36 +01003362 curbuf->b_ml.ml_flags &= ~ML_LINE_DIRTY;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003363
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003364#ifdef FEAT_PROP_POPUP
Bram Moolenaarccae4672019-01-04 15:09:57 +01003365 if (curbuf->b_has_textprop && !has_props)
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003366 // Need to fetch the old line to copy over any text properties.
3367 ml_get_buf(curbuf, lnum, TRUE);
3368#endif
3369 }
3370
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003371#ifdef FEAT_PROP_POPUP
Bram Moolenaarccae4672019-01-04 15:09:57 +01003372 if (curbuf->b_has_textprop && !has_props)
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003373 {
3374 size_t oldtextlen = STRLEN(curbuf->b_ml.ml_line_ptr) + 1;
3375
3376 if (oldtextlen < (size_t)curbuf->b_ml.ml_line_len)
3377 {
3378 char_u *newline;
3379 size_t textproplen = curbuf->b_ml.ml_line_len - oldtextlen;
3380
3381 // Need to copy over text properties, stored after the text.
Bram Moolenaarccae4672019-01-04 15:09:57 +01003382 newline = alloc(len + (int)textproplen);
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003383 if (newline != NULL)
3384 {
Bram Moolenaarccae4672019-01-04 15:09:57 +01003385 mch_memmove(newline, line, len);
Bram Moolenaar0d3cb732019-05-18 13:05:18 +02003386 mch_memmove(newline + len, curbuf->b_ml.ml_line_ptr
3387 + oldtextlen, textproplen);
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003388 vim_free(line);
3389 line = newline;
Bram Moolenaar4efe73b2018-12-16 14:37:39 +01003390 len += (colnr_T)textproplen;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003391 }
3392 }
3393 }
3394#endif
3395
Bram Moolenaarccae4672019-01-04 15:09:57 +01003396 if (curbuf->b_ml.ml_flags & ML_LINE_DIRTY) // same line allocated
3397 vim_free(curbuf->b_ml.ml_line_ptr); // free it
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003398
Bram Moolenaar071d4272004-06-13 20:20:40 +00003399 curbuf->b_ml.ml_line_ptr = line;
Bram Moolenaarccae4672019-01-04 15:09:57 +01003400 curbuf->b_ml.ml_line_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003401 curbuf->b_ml.ml_line_lnum = lnum;
3402 curbuf->b_ml.ml_flags = (curbuf->b_ml.ml_flags | ML_LINE_DIRTY) & ~ML_EMPTY;
3403
3404 return OK;
3405}
3406
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003407#ifdef FEAT_PROP_POPUP
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003408/*
3409 * Adjust text properties in line "lnum" for a deleted line.
3410 * When "above" is true this is the line above the deleted line.
3411 * "del_props" are the properties of the deleted line.
3412 */
3413 static void
3414adjust_text_props_for_delete(
3415 buf_T *buf,
3416 linenr_T lnum,
3417 char_u *del_props,
3418 int del_props_len,
3419 int above)
3420{
3421 int did_get_line = FALSE;
3422 int done_del;
3423 int done_this;
3424 textprop_T prop_del;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003425 bhdr_T *hp;
3426 DATA_BL *dp;
3427 int idx;
3428 int line_start;
3429 long line_size;
3430 int this_props_len;
3431 char_u *text;
3432 size_t textlen;
3433 int found;
3434
3435 for (done_del = 0; done_del < del_props_len; done_del += sizeof(textprop_T))
3436 {
3437 mch_memmove(&prop_del, del_props + done_del, sizeof(textprop_T));
3438 if ((above && (prop_del.tp_flags & TP_FLAG_CONT_PREV)
3439 && !(prop_del.tp_flags & TP_FLAG_CONT_NEXT))
3440 || (!above && (prop_del.tp_flags & TP_FLAG_CONT_NEXT)
3441 && !(prop_del.tp_flags & TP_FLAG_CONT_PREV)))
3442 {
3443 if (!did_get_line)
3444 {
3445 did_get_line = TRUE;
3446 if ((hp = ml_find_line(buf, lnum, ML_FIND)) == NULL)
3447 return;
3448
3449 dp = (DATA_BL *)(hp->bh_data);
3450 idx = lnum - buf->b_ml.ml_locked_low;
3451 line_start = ((dp->db_index[idx]) & DB_INDEX_MASK);
3452 if (idx == 0) // first line in block, text at the end
3453 line_size = dp->db_txt_end - line_start;
3454 else
Bram Moolenaar87be9be2020-05-30 15:32:02 +02003455 line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK)
3456 - line_start;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003457 text = (char_u *)dp + line_start;
3458 textlen = STRLEN(text) + 1;
3459 if ((long)textlen >= line_size)
3460 {
3461 if (above)
3462 internal_error("no text property above deleted line");
3463 else
3464 internal_error("no text property below deleted line");
3465 return;
3466 }
Bram Moolenaar4b7214e2019-01-03 21:55:32 +01003467 this_props_len = line_size - (int)textlen;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003468 }
3469
3470 found = FALSE;
Bram Moolenaar87be9be2020-05-30 15:32:02 +02003471 for (done_this = 0; done_this < this_props_len;
3472 done_this += sizeof(textprop_T))
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003473 {
Bram Moolenaar87be9be2020-05-30 15:32:02 +02003474 int flag = above ? TP_FLAG_CONT_NEXT
3475 : TP_FLAG_CONT_PREV;
3476 textprop_T prop_this;
3477
3478 mch_memmove(&prop_this, text + textlen + done_del,
3479 sizeof(textprop_T));
3480 if ((prop_this.tp_flags & flag)
3481 && prop_del.tp_id == prop_this.tp_id
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003482 && prop_del.tp_type == prop_this.tp_type)
3483 {
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003484 found = TRUE;
Bram Moolenaar87be9be2020-05-30 15:32:02 +02003485 prop_this.tp_flags &= ~flag;
3486 mch_memmove(text + textlen + done_del, &prop_this,
3487 sizeof(textprop_T));
3488 break;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003489 }
3490 }
3491 if (!found)
3492 {
3493 if (above)
3494 internal_error("text property above deleted line not found");
3495 else
3496 internal_error("text property below deleted line not found");
3497 }
3498
3499 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
3500 }
3501 }
3502}
3503#endif
3504
Bram Moolenaar071d4272004-06-13 20:20:40 +00003505/*
Bram Moolenaar4033c552017-09-16 20:54:51 +02003506 * Delete line "lnum" in the current buffer.
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003507 * When "flags" has ML_DEL_MESSAGE may give a "No lines in buffer" message.
3508 * When "flags" has ML_DEL_UNDO this is called from undo.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003509 *
3510 * return FAIL for failure, OK otherwise
3511 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003512 static int
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003513ml_delete_int(buf_T *buf, linenr_T lnum, int flags)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003514{
3515 bhdr_T *hp;
3516 memfile_T *mfp;
3517 DATA_BL *dp;
3518 PTR_BL *pp;
3519 infoptr_T *ip;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003520 int count; // number of entries in block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003521 int idx;
3522 int stack_idx;
3523 int text_start;
3524 int line_start;
3525 long line_size;
3526 int i;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003527 int ret = FAIL;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003528#ifdef FEAT_PROP_POPUP
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003529 char_u *textprop_save = NULL;
3530 int textprop_save_len;
3531#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003532
Bram Moolenaar071d4272004-06-13 20:20:40 +00003533 if (lowest_marked && lowest_marked > lnum)
3534 lowest_marked--;
3535
3536/*
3537 * If the file becomes empty the last line is replaced by an empty line.
3538 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003539 if (buf->b_ml.ml_line_count == 1) // file becomes empty
Bram Moolenaar071d4272004-06-13 20:20:40 +00003540 {
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003541 if ((flags & ML_DEL_MESSAGE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003542#ifdef FEAT_NETBEANS_INTG
3543 && !netbeansSuppressNoLines
3544#endif
3545 )
Bram Moolenaar238a5642006-02-21 22:12:05 +00003546 set_keep_msg((char_u *)_(no_lines_msg), 0);
3547
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003548 // FEAT_BYTEOFF already handled in there, don't worry 'bout it below
Bram Moolenaar071d4272004-06-13 20:20:40 +00003549 i = ml_replace((linenr_T)1, (char_u *)"", TRUE);
3550 buf->b_ml.ml_flags |= ML_EMPTY;
3551
3552 return i;
3553 }
3554
3555/*
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003556 * Find the data block containing the line.
3557 * This also fills the stack with the blocks from the root to the data block.
3558 * This also releases any locked block..
Bram Moolenaar071d4272004-06-13 20:20:40 +00003559 */
3560 mfp = buf->b_ml.ml_mfp;
3561 if (mfp == NULL)
3562 return FAIL;
3563
3564 if ((hp = ml_find_line(buf, lnum, ML_DELETE)) == NULL)
3565 return FAIL;
3566
3567 dp = (DATA_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003568 // compute line count before the delete
Bram Moolenaar071d4272004-06-13 20:20:40 +00003569 count = (long)(buf->b_ml.ml_locked_high)
3570 - (long)(buf->b_ml.ml_locked_low) + 2;
3571 idx = lnum - buf->b_ml.ml_locked_low;
3572
3573 --buf->b_ml.ml_line_count;
3574
3575 line_start = ((dp->db_index[idx]) & DB_INDEX_MASK);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003576 if (idx == 0) // first line in block, text at the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00003577 line_size = dp->db_txt_end - line_start;
3578 else
3579 line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK) - line_start;
3580
3581#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02003582 if (netbeans_active())
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00003583 netbeans_removed(buf, lnum, 0, (long)line_size);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003584#endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003585#ifdef FEAT_PROP_POPUP
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003586 // If there are text properties, make a copy, so that we can update
3587 // properties in preceding and following lines.
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003588 if (buf->b_has_textprop && !(flags & ML_DEL_UNDO))
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003589 {
3590 size_t textlen = STRLEN((char_u *)dp + line_start) + 1;
3591
3592 if ((long)textlen < line_size)
3593 {
Bram Moolenaar4b7214e2019-01-03 21:55:32 +01003594 textprop_save_len = line_size - (int)textlen;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003595 textprop_save = vim_memsave((char_u *)dp + line_start + textlen,
3596 textprop_save_len);
3597 }
3598 }
3599#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003600
3601/*
3602 * special case: If there is only one line in the data block it becomes empty.
3603 * Then we have to remove the entry, pointing to this data block, from the
3604 * pointer block. If this pointer block also becomes empty, we go up another
3605 * block, and so on, up to the root if necessary.
3606 * The line counts in the pointer blocks have already been adjusted by
3607 * ml_find_line().
3608 */
3609 if (count == 1)
3610 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003611 mf_free(mfp, hp); // free the data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003612 buf->b_ml.ml_locked = NULL;
3613
Bram Moolenaare60acc12011-05-10 16:41:25 +02003614 for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0;
3615 --stack_idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003616 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003617 buf->b_ml.ml_stack_top = 0; // stack is invalid when failing
Bram Moolenaar071d4272004-06-13 20:20:40 +00003618 ip = &(buf->b_ml.ml_stack[stack_idx]);
3619 idx = ip->ip_index;
3620 if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003621 goto theend;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003622 pp = (PTR_BL *)(hp->bh_data); // must be pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003623 if (pp->pb_id != PTR_ID)
3624 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003625 iemsg(_("E317: pointer block id wrong 4"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003626 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003627 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003628 }
3629 count = --(pp->pb_count);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003630 if (count == 0) // the pointer block becomes empty!
Bram Moolenaar071d4272004-06-13 20:20:40 +00003631 mf_free(mfp, hp);
3632 else
3633 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003634 if (count != idx) // move entries after the deleted one
Bram Moolenaar071d4272004-06-13 20:20:40 +00003635 mch_memmove(&pp->pb_pointer[idx], &pp->pb_pointer[idx + 1],
3636 (size_t)(count - idx) * sizeof(PTR_EN));
3637 mf_put(mfp, hp, TRUE, FALSE);
3638
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003639 buf->b_ml.ml_stack_top = stack_idx; // truncate stack
3640 // fix line count for rest of blocks in the stack
Bram Moolenaar6b803a72007-05-06 14:25:46 +00003641 if (buf->b_ml.ml_locked_lineadd != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003642 {
3643 ml_lineadd(buf, buf->b_ml.ml_locked_lineadd);
3644 buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high +=
Bram Moolenaar6b803a72007-05-06 14:25:46 +00003645 buf->b_ml.ml_locked_lineadd;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003646 }
3647 ++(buf->b_ml.ml_stack_top);
3648
3649 break;
3650 }
3651 }
3652 CHECK(stack_idx < 0, _("deleted block 1?"));
3653 }
3654 else
3655 {
3656 /*
3657 * delete the text by moving the next lines forwards
3658 */
3659 text_start = dp->db_txt_start;
3660 mch_memmove((char *)dp + text_start + line_size,
3661 (char *)dp + text_start, (size_t)(line_start - text_start));
3662
3663 /*
3664 * delete the index by moving the next indexes backwards
3665 * Adjust the indexes for the text movement.
3666 */
3667 for (i = idx; i < count - 1; ++i)
3668 dp->db_index[i] = dp->db_index[i + 1] + line_size;
3669
3670 dp->db_free += line_size + INDEX_SIZE;
3671 dp->db_txt_start += line_size;
3672 --(dp->db_line_count);
3673
3674 /*
3675 * mark the block dirty and make sure it is in the file (for recovery)
3676 */
3677 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
3678 }
3679
3680#ifdef FEAT_BYTEOFF
3681 ml_updatechunk(buf, lnum, line_size, ML_CHNK_DELLINE);
3682#endif
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003683 ret = OK;
3684
3685theend:
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01003686#ifdef FEAT_PROP_POPUP
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003687 if (textprop_save != NULL)
3688 {
3689 // Adjust text properties in the line above and below.
3690 if (lnum > 1)
3691 adjust_text_props_for_delete(buf, lnum - 1, textprop_save, textprop_save_len, TRUE);
3692 if (lnum <= buf->b_ml.ml_line_count)
3693 adjust_text_props_for_delete(buf, lnum, textprop_save, textprop_save_len, FALSE);
3694 }
3695 vim_free(textprop_save);
3696#endif
3697 return ret;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003698}
3699
3700/*
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003701 * Delete line "lnum" in the current buffer.
3702 * When "message" is TRUE may give a "No lines in buffer" message.
3703 *
3704 * Check: The caller of this function should probably also call
3705 * deleted_lines() after this.
3706 *
3707 * return FAIL for failure, OK otherwise
3708 */
3709 int
Bram Moolenaarca70c072020-05-30 20:30:46 +02003710ml_delete(linenr_T lnum)
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003711{
Bram Moolenaarca70c072020-05-30 20:30:46 +02003712 return ml_delete_flags(lnum, 0);
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003713}
3714
3715/*
3716 * Like ml_delete() but using flags (see ml_delete_int()).
3717 */
3718 int
3719ml_delete_flags(linenr_T lnum, int flags)
3720{
3721 ml_flush_line(curbuf);
3722 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count)
3723 return FAIL;
3724
3725#ifdef FEAT_EVAL
3726 // When inserting above recorded changes: flush the changes before changing
3727 // the text.
3728 may_invoke_listeners(curbuf, lnum, lnum + 1, -1);
3729#endif
3730
3731 return ml_delete_int(curbuf, lnum, flags);
3732}
3733
3734/*
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003735 * set the DB_MARKED flag for line 'lnum'
Bram Moolenaar071d4272004-06-13 20:20:40 +00003736 */
3737 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003738ml_setmarked(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003739{
3740 bhdr_T *hp;
3741 DATA_BL *dp;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003742 // invalid line number
Bram Moolenaar071d4272004-06-13 20:20:40 +00003743 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count
3744 || curbuf->b_ml.ml_mfp == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003745 return; // give error message?
Bram Moolenaar071d4272004-06-13 20:20:40 +00003746
3747 if (lowest_marked == 0 || lowest_marked > lnum)
3748 lowest_marked = lnum;
3749
3750 /*
3751 * find the data block containing the line
3752 * This also fills the stack with the blocks from the root to the data block
3753 * This also releases any locked block.
3754 */
3755 if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003756 return; // give error message?
Bram Moolenaar071d4272004-06-13 20:20:40 +00003757
3758 dp = (DATA_BL *)(hp->bh_data);
3759 dp->db_index[lnum - curbuf->b_ml.ml_locked_low] |= DB_MARKED;
3760 curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
3761}
3762
3763/*
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003764 * find the first line with its DB_MARKED flag set
Bram Moolenaar071d4272004-06-13 20:20:40 +00003765 */
3766 linenr_T
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003767ml_firstmarked(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003768{
3769 bhdr_T *hp;
3770 DATA_BL *dp;
3771 linenr_T lnum;
3772 int i;
3773
3774 if (curbuf->b_ml.ml_mfp == NULL)
3775 return (linenr_T) 0;
3776
3777 /*
3778 * The search starts with lowest_marked line. This is the last line where
3779 * a mark was found, adjusted by inserting/deleting lines.
3780 */
3781 for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count; )
3782 {
3783 /*
3784 * Find the data block containing the line.
3785 * This also fills the stack with the blocks from the root to the data
3786 * block This also releases any locked block.
3787 */
3788 if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003789 return (linenr_T)0; // give error message?
Bram Moolenaar071d4272004-06-13 20:20:40 +00003790
3791 dp = (DATA_BL *)(hp->bh_data);
3792
3793 for (i = lnum - curbuf->b_ml.ml_locked_low;
3794 lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum)
3795 if ((dp->db_index[i]) & DB_MARKED)
3796 {
3797 (dp->db_index[i]) &= DB_INDEX_MASK;
3798 curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
3799 lowest_marked = lnum + 1;
3800 return lnum;
3801 }
3802 }
3803
3804 return (linenr_T) 0;
3805}
3806
Bram Moolenaar071d4272004-06-13 20:20:40 +00003807/*
3808 * clear all DB_MARKED flags
3809 */
3810 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003811ml_clearmarked(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003812{
3813 bhdr_T *hp;
3814 DATA_BL *dp;
3815 linenr_T lnum;
3816 int i;
3817
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003818 if (curbuf->b_ml.ml_mfp == NULL) // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00003819 return;
3820
3821 /*
3822 * The search starts with line lowest_marked.
3823 */
3824 for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count; )
3825 {
3826 /*
3827 * Find the data block containing the line.
3828 * This also fills the stack with the blocks from the root to the data
3829 * block and releases any locked block.
3830 */
3831 if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003832 return; // give error message?
Bram Moolenaar071d4272004-06-13 20:20:40 +00003833
3834 dp = (DATA_BL *)(hp->bh_data);
3835
3836 for (i = lnum - curbuf->b_ml.ml_locked_low;
3837 lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum)
3838 if ((dp->db_index[i]) & DB_MARKED)
3839 {
3840 (dp->db_index[i]) &= DB_INDEX_MASK;
3841 curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
3842 }
3843 }
3844
3845 lowest_marked = 0;
3846 return;
3847}
3848
3849/*
3850 * flush ml_line if necessary
3851 */
3852 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003853ml_flush_line(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003854{
3855 bhdr_T *hp;
3856 DATA_BL *dp;
3857 linenr_T lnum;
3858 char_u *new_line;
3859 char_u *old_line;
3860 colnr_T new_len;
3861 int old_len;
3862 int extra;
3863 int idx;
3864 int start;
3865 int count;
3866 int i;
Bram Moolenaar0ca4b352010-02-11 18:54:43 +01003867 static int entered = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003868
3869 if (buf->b_ml.ml_line_lnum == 0 || buf->b_ml.ml_mfp == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003870 return; // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00003871
3872 if (buf->b_ml.ml_flags & ML_LINE_DIRTY)
3873 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003874 // This code doesn't work recursively, but Netbeans may call back here
3875 // when obtaining the cursor position.
Bram Moolenaar0ca4b352010-02-11 18:54:43 +01003876 if (entered)
3877 return;
3878 entered = TRUE;
3879
Bram Moolenaar071d4272004-06-13 20:20:40 +00003880 lnum = buf->b_ml.ml_line_lnum;
3881 new_line = buf->b_ml.ml_line_ptr;
3882
3883 hp = ml_find_line(buf, lnum, ML_FIND);
3884 if (hp == NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003885 siemsg(_("E320: Cannot find line %ld"), lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003886 else
3887 {
3888 dp = (DATA_BL *)(hp->bh_data);
3889 idx = lnum - buf->b_ml.ml_locked_low;
3890 start = ((dp->db_index[idx]) & DB_INDEX_MASK);
3891 old_line = (char_u *)dp + start;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003892 if (idx == 0) // line is last in block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003893 old_len = dp->db_txt_end - start;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003894 else // text of previous line follows
Bram Moolenaar071d4272004-06-13 20:20:40 +00003895 old_len = (dp->db_index[idx - 1] & DB_INDEX_MASK) - start;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003896 new_len = buf->b_ml.ml_line_len;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003897 extra = new_len - old_len; // negative if lines gets smaller
Bram Moolenaar071d4272004-06-13 20:20:40 +00003898
3899 /*
3900 * if new line fits in data block, replace directly
3901 */
3902 if ((int)dp->db_free >= extra)
3903 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003904 // if the length changes and there are following lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00003905 count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 1;
3906 if (extra != 0 && idx < count - 1)
3907 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003908 // move text of following lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00003909 mch_memmove((char *)dp + dp->db_txt_start - extra,
3910 (char *)dp + dp->db_txt_start,
3911 (size_t)(start - dp->db_txt_start));
3912
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003913 // adjust pointers of this and following lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00003914 for (i = idx + 1; i < count; ++i)
3915 dp->db_index[i] -= extra;
3916 }
3917 dp->db_index[idx] -= extra;
3918
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003919 // adjust free space
Bram Moolenaar071d4272004-06-13 20:20:40 +00003920 dp->db_free -= extra;
3921 dp->db_txt_start -= extra;
3922
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003923 // copy new line into the data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00003924 mch_memmove(old_line - extra, new_line, (size_t)new_len);
3925 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
3926#ifdef FEAT_BYTEOFF
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003927 // The else case is already covered by the insert and delete
Bram Moolenaar071d4272004-06-13 20:20:40 +00003928 ml_updatechunk(buf, lnum, (long)extra, ML_CHNK_UPDLINE);
3929#endif
3930 }
3931 else
3932 {
3933 /*
3934 * Cannot do it in one data block: Delete and append.
3935 * Append first, because ml_delete_int() cannot delete the
3936 * last line in a buffer, which causes trouble for a buffer
3937 * that has only one line.
3938 * Don't forget to copy the mark!
3939 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01003940 // How about handling errors???
Bram Moolenaara9d4b842020-05-30 14:46:52 +02003941 (void)ml_append_int(buf, lnum, new_line, new_len,
3942 (dp->db_index[idx] & DB_MARKED) ? ML_APPEND_MARK : 0);
3943 (void)ml_delete_int(buf, lnum, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003944 }
3945 }
3946 vim_free(new_line);
Bram Moolenaar0ca4b352010-02-11 18:54:43 +01003947
3948 entered = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003949 }
3950
3951 buf->b_ml.ml_line_lnum = 0;
3952}
3953
3954/*
3955 * create a new, empty, data block
3956 */
3957 static bhdr_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003958ml_new_data(memfile_T *mfp, int negative, int page_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003959{
3960 bhdr_T *hp;
3961 DATA_BL *dp;
3962
3963 if ((hp = mf_new(mfp, negative, page_count)) == NULL)
3964 return NULL;
3965
3966 dp = (DATA_BL *)(hp->bh_data);
3967 dp->db_id = DATA_ID;
3968 dp->db_txt_start = dp->db_txt_end = page_count * mfp->mf_page_size;
3969 dp->db_free = dp->db_txt_start - HEADER_SIZE;
3970 dp->db_line_count = 0;
3971
3972 return hp;
3973}
3974
3975/*
3976 * create a new, empty, pointer block
3977 */
3978 static bhdr_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003979ml_new_ptr(memfile_T *mfp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003980{
3981 bhdr_T *hp;
3982 PTR_BL *pp;
3983
3984 if ((hp = mf_new(mfp, FALSE, 1)) == NULL)
3985 return NULL;
3986
3987 pp = (PTR_BL *)(hp->bh_data);
3988 pp->pb_id = PTR_ID;
3989 pp->pb_count = 0;
Bram Moolenaar20a825a2010-05-31 21:27:30 +02003990 pp->pb_count_max = (short_u)((mfp->mf_page_size - sizeof(PTR_BL))
3991 / sizeof(PTR_EN) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003992
3993 return hp;
3994}
3995
3996/*
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003997 * Lookup line 'lnum' in a memline.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003998 *
3999 * action: if ML_DELETE or ML_INSERT the line count is updated while searching
4000 * if ML_FLUSH only flush a locked block
4001 * if ML_FIND just find the line
4002 *
4003 * If the block was found it is locked and put in ml_locked.
4004 * The stack is updated to lead to the locked block. The ip_high field in
4005 * the stack is updated to reflect the last line in the block AFTER the
4006 * insert or delete, also if the pointer block has not been updated yet. But
Bram Moolenaar6b803a72007-05-06 14:25:46 +00004007 * if ml_locked != NULL ml_locked_lineadd must be added to ip_high.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004008 *
4009 * return: NULL for failure, pointer to block header otherwise
4010 */
4011 static bhdr_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004012ml_find_line(buf_T *buf, linenr_T lnum, int action)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004013{
4014 DATA_BL *dp;
4015 PTR_BL *pp;
4016 infoptr_T *ip;
4017 bhdr_T *hp;
4018 memfile_T *mfp;
4019 linenr_T t;
4020 blocknr_T bnum, bnum2;
4021 int dirty;
4022 linenr_T low, high;
4023 int top;
4024 int page_count;
4025 int idx;
4026
4027 mfp = buf->b_ml.ml_mfp;
4028
4029 /*
4030 * If there is a locked block check if the wanted line is in it.
4031 * If not, flush and release the locked block.
4032 * Don't do this for ML_INSERT_SAME, because the stack need to be updated.
4033 * Don't do this for ML_FLUSH, because we want to flush the locked block.
Bram Moolenaar47b8b152007-02-07 02:41:57 +00004034 * Don't do this when 'swapfile' is reset, we want to load all the blocks.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004035 */
4036 if (buf->b_ml.ml_locked)
4037 {
Bram Moolenaar47b8b152007-02-07 02:41:57 +00004038 if (ML_SIMPLE(action)
4039 && buf->b_ml.ml_locked_low <= lnum
4040 && buf->b_ml.ml_locked_high >= lnum
4041 && !mf_dont_release)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004042 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004043 // remember to update pointer blocks and stack later
Bram Moolenaar071d4272004-06-13 20:20:40 +00004044 if (action == ML_INSERT)
4045 {
4046 ++(buf->b_ml.ml_locked_lineadd);
4047 ++(buf->b_ml.ml_locked_high);
4048 }
4049 else if (action == ML_DELETE)
4050 {
4051 --(buf->b_ml.ml_locked_lineadd);
4052 --(buf->b_ml.ml_locked_high);
4053 }
4054 return (buf->b_ml.ml_locked);
4055 }
4056
4057 mf_put(mfp, buf->b_ml.ml_locked, buf->b_ml.ml_flags & ML_LOCKED_DIRTY,
4058 buf->b_ml.ml_flags & ML_LOCKED_POS);
4059 buf->b_ml.ml_locked = NULL;
4060
Bram Moolenaar6b803a72007-05-06 14:25:46 +00004061 /*
4062 * If lines have been added or deleted in the locked block, need to
4063 * update the line count in pointer blocks.
4064 */
4065 if (buf->b_ml.ml_locked_lineadd != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004066 ml_lineadd(buf, buf->b_ml.ml_locked_lineadd);
4067 }
4068
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004069 if (action == ML_FLUSH) // nothing else to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00004070 return NULL;
4071
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004072 bnum = 1; // start at the root of the tree
Bram Moolenaar071d4272004-06-13 20:20:40 +00004073 page_count = 1;
4074 low = 1;
4075 high = buf->b_ml.ml_line_count;
4076
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004077 if (action == ML_FIND) // first try stack entries
Bram Moolenaar071d4272004-06-13 20:20:40 +00004078 {
4079 for (top = buf->b_ml.ml_stack_top - 1; top >= 0; --top)
4080 {
4081 ip = &(buf->b_ml.ml_stack[top]);
4082 if (ip->ip_low <= lnum && ip->ip_high >= lnum)
4083 {
4084 bnum = ip->ip_bnum;
4085 low = ip->ip_low;
4086 high = ip->ip_high;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004087 buf->b_ml.ml_stack_top = top; // truncate stack at prev entry
Bram Moolenaar071d4272004-06-13 20:20:40 +00004088 break;
4089 }
4090 }
4091 if (top < 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004092 buf->b_ml.ml_stack_top = 0; // not found, start at the root
Bram Moolenaar071d4272004-06-13 20:20:40 +00004093 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004094 else // ML_DELETE or ML_INSERT
4095 buf->b_ml.ml_stack_top = 0; // start at the root
Bram Moolenaar071d4272004-06-13 20:20:40 +00004096
4097/*
4098 * search downwards in the tree until a data block is found
4099 */
4100 for (;;)
4101 {
4102 if ((hp = mf_get(mfp, bnum, page_count)) == NULL)
4103 goto error_noblock;
4104
4105 /*
4106 * update high for insert/delete
4107 */
4108 if (action == ML_INSERT)
4109 ++high;
4110 else if (action == ML_DELETE)
4111 --high;
4112
4113 dp = (DATA_BL *)(hp->bh_data);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004114 if (dp->db_id == DATA_ID) // data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00004115 {
4116 buf->b_ml.ml_locked = hp;
4117 buf->b_ml.ml_locked_low = low;
4118 buf->b_ml.ml_locked_high = high;
4119 buf->b_ml.ml_locked_lineadd = 0;
4120 buf->b_ml.ml_flags &= ~(ML_LOCKED_DIRTY | ML_LOCKED_POS);
4121 return hp;
4122 }
4123
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004124 pp = (PTR_BL *)(dp); // must be pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00004125 if (pp->pb_id != PTR_ID)
4126 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004127 iemsg(_("E317: pointer block id wrong"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004128 goto error_block;
4129 }
4130
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004131 if ((top = ml_add_stack(buf)) < 0) // add new entry to stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00004132 goto error_block;
4133 ip = &(buf->b_ml.ml_stack[top]);
4134 ip->ip_bnum = bnum;
4135 ip->ip_low = low;
4136 ip->ip_high = high;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004137 ip->ip_index = -1; // index not known yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00004138
4139 dirty = FALSE;
4140 for (idx = 0; idx < (int)pp->pb_count; ++idx)
4141 {
4142 t = pp->pb_pointer[idx].pe_line_count;
4143 CHECK(t == 0, _("pe_line_count is zero"));
4144 if ((low += t) > lnum)
4145 {
4146 ip->ip_index = idx;
4147 bnum = pp->pb_pointer[idx].pe_bnum;
4148 page_count = pp->pb_pointer[idx].pe_page_count;
4149 high = low - 1;
4150 low -= t;
4151
4152 /*
4153 * a negative block number may have been changed
4154 */
4155 if (bnum < 0)
4156 {
4157 bnum2 = mf_trans_del(mfp, bnum);
4158 if (bnum != bnum2)
4159 {
4160 bnum = bnum2;
4161 pp->pb_pointer[idx].pe_bnum = bnum;
4162 dirty = TRUE;
4163 }
4164 }
4165
4166 break;
4167 }
4168 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004169 if (idx >= (int)pp->pb_count) // past the end: something wrong!
Bram Moolenaar071d4272004-06-13 20:20:40 +00004170 {
4171 if (lnum > buf->b_ml.ml_line_count)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004172 siemsg(_("E322: line number out of range: %ld past the end"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00004173 lnum - buf->b_ml.ml_line_count);
4174
4175 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004176 siemsg(_("E323: line count wrong in block %ld"), bnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004177 goto error_block;
4178 }
4179 if (action == ML_DELETE)
4180 {
4181 pp->pb_pointer[idx].pe_line_count--;
4182 dirty = TRUE;
4183 }
4184 else if (action == ML_INSERT)
4185 {
4186 pp->pb_pointer[idx].pe_line_count++;
4187 dirty = TRUE;
4188 }
4189 mf_put(mfp, hp, dirty, FALSE);
4190 }
4191
4192error_block:
4193 mf_put(mfp, hp, FALSE, FALSE);
4194error_noblock:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02004195 /*
4196 * If action is ML_DELETE or ML_INSERT we have to correct the tree for
4197 * the incremented/decremented line counts, because there won't be a line
4198 * inserted/deleted after all.
4199 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004200 if (action == ML_DELETE)
4201 ml_lineadd(buf, 1);
4202 else if (action == ML_INSERT)
4203 ml_lineadd(buf, -1);
4204 buf->b_ml.ml_stack_top = 0;
4205 return NULL;
4206}
4207
4208/*
4209 * add an entry to the info pointer stack
4210 *
4211 * return -1 for failure, number of the new entry otherwise
4212 */
4213 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004214ml_add_stack(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004215{
4216 int top;
4217 infoptr_T *newstack;
4218
4219 top = buf->b_ml.ml_stack_top;
4220
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004221 // may have to increase the stack size
Bram Moolenaar071d4272004-06-13 20:20:40 +00004222 if (top == buf->b_ml.ml_stack_size)
4223 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004224 CHECK(top > 0, _("Stack size increases")); // more than 5 levels???
Bram Moolenaar071d4272004-06-13 20:20:40 +00004225
Bram Moolenaarc799fe22019-05-28 23:08:19 +02004226 newstack = ALLOC_MULT(infoptr_T, buf->b_ml.ml_stack_size + STACK_INCR);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004227 if (newstack == NULL)
4228 return -1;
Bram Moolenaarfbd302f2015-08-08 18:23:46 +02004229 if (top > 0)
4230 mch_memmove(newstack, buf->b_ml.ml_stack,
Bram Moolenaar8c8de832008-06-24 22:58:06 +00004231 (size_t)top * sizeof(infoptr_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004232 vim_free(buf->b_ml.ml_stack);
4233 buf->b_ml.ml_stack = newstack;
4234 buf->b_ml.ml_stack_size += STACK_INCR;
4235 }
4236
4237 buf->b_ml.ml_stack_top++;
4238 return top;
4239}
4240
4241/*
4242 * Update the pointer blocks on the stack for inserted/deleted lines.
4243 * The stack itself is also updated.
4244 *
4245 * When a insert/delete line action fails, the line is not inserted/deleted,
4246 * but the pointer blocks have already been updated. That is fixed here by
4247 * walking through the stack.
4248 *
4249 * Count is the number of lines added, negative if lines have been deleted.
4250 */
4251 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004252ml_lineadd(buf_T *buf, int count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004253{
4254 int idx;
4255 infoptr_T *ip;
4256 PTR_BL *pp;
4257 memfile_T *mfp = buf->b_ml.ml_mfp;
4258 bhdr_T *hp;
4259
4260 for (idx = buf->b_ml.ml_stack_top - 1; idx >= 0; --idx)
4261 {
4262 ip = &(buf->b_ml.ml_stack[idx]);
4263 if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
4264 break;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004265 pp = (PTR_BL *)(hp->bh_data); // must be pointer block
Bram Moolenaar071d4272004-06-13 20:20:40 +00004266 if (pp->pb_id != PTR_ID)
4267 {
4268 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004269 iemsg(_("E317: pointer block id wrong 2"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004270 break;
4271 }
4272 pp->pb_pointer[ip->ip_index].pe_line_count += count;
4273 ip->ip_high += count;
4274 mf_put(mfp, hp, TRUE, FALSE);
4275 }
4276}
4277
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004278#if defined(HAVE_READLINK) || defined(PROTO)
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004279/*
4280 * Resolve a symlink in the last component of a file name.
4281 * Note that f_resolve() does it for every part of the path, we don't do that
4282 * here.
4283 * If it worked returns OK and the resolved link in "buf[MAXPATHL]".
4284 * Otherwise returns FAIL.
4285 */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004286 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004287resolve_symlink(char_u *fname, char_u *buf)
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004288{
4289 char_u tmp[MAXPATHL];
4290 int ret;
4291 int depth = 0;
4292
4293 if (fname == NULL)
4294 return FAIL;
4295
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004296 // Put the result so far in tmp[], starting with the original name.
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004297 vim_strncpy(tmp, fname, MAXPATHL - 1);
4298
4299 for (;;)
4300 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004301 // Limit symlink depth to 100, catch recursive loops.
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004302 if (++depth == 100)
4303 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004304 semsg(_("E773: Symlink loop for \"%s\""), fname);
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004305 return FAIL;
4306 }
4307
4308 ret = readlink((char *)tmp, (char *)buf, MAXPATHL - 1);
4309 if (ret <= 0)
4310 {
Bram Moolenaarcc984262005-12-23 22:19:46 +00004311 if (errno == EINVAL || errno == ENOENT)
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004312 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004313 // Found non-symlink or not existing file, stop here.
4314 // When at the first level use the unmodified name, skip the
4315 // call to vim_FullName().
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004316 if (depth == 1)
4317 return FAIL;
4318
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004319 // Use the resolved name in tmp[].
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004320 break;
4321 }
4322
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004323 // There must be some error reading links, use original name.
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004324 return FAIL;
4325 }
4326 buf[ret] = NUL;
4327
4328 /*
4329 * Check whether the symlink is relative or absolute.
4330 * If it's relative, build a new path based on the directory
4331 * portion of the filename (if any) and the path the symlink
4332 * points to.
4333 */
4334 if (mch_isFullName(buf))
4335 STRCPY(tmp, buf);
4336 else
4337 {
4338 char_u *tail;
4339
4340 tail = gettail(tmp);
4341 if (STRLEN(tail) + STRLEN(buf) >= MAXPATHL)
4342 return FAIL;
4343 STRCPY(tail, buf);
4344 }
4345 }
4346
4347 /*
4348 * Try to resolve the full name of the file so that the swapfile name will
4349 * be consistent even when opening a relative symlink from different
4350 * working directories.
4351 */
4352 return vim_FullName(tmp, buf, MAXPATHL, TRUE);
4353}
4354#endif
4355
Bram Moolenaar071d4272004-06-13 20:20:40 +00004356/*
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004357 * Make swap file name out of the file name and a directory name.
4358 * Returns pointer to allocated memory or NULL.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004359 */
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004360 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004361makeswapname(
4362 char_u *fname,
4363 char_u *ffname UNUSED,
4364 buf_T *buf,
4365 char_u *dir_name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004366{
4367 char_u *r, *s;
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02004368 char_u *fname_res = fname;
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004369#ifdef HAVE_READLINK
4370 char_u fname_buf[MAXPATHL];
Bram Moolenaar5966ea12020-07-15 15:30:05 +02004371
4372 // Expand symlink in the file name, so that we put the swap file with the
4373 // actual file instead of with the symlink.
4374 if (resolve_symlink(fname, fname_buf) == OK)
4375 fname_res = fname_buf;
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004376#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004377
Bram Moolenaar4f974752019-02-17 17:44:42 +01004378#if defined(UNIX) || defined(MSWIN) // Need _very_ long file names
Bram Moolenaarb113c3a2017-02-28 21:26:17 +01004379 int len = (int)STRLEN(dir_name);
Bram Moolenaarc525e3a2017-02-18 16:59:02 +01004380
4381 s = dir_name + len;
4382 if (after_pathsep(dir_name, s) && len > 1 && s[-1] == s[-2])
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004383 { // Ends with '//', Use Full path
Bram Moolenaar071d4272004-06-13 20:20:40 +00004384 r = NULL;
Bram Moolenaar5966ea12020-07-15 15:30:05 +02004385 if ((s = make_percent_swname(dir_name, fname_res)) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004386 {
4387 r = modname(s, (char_u *)".swp", FALSE);
4388 vim_free(s);
4389 }
4390 return r;
4391 }
4392#endif
4393
4394 r = buf_modname(
Bram Moolenaar071d4272004-06-13 20:20:40 +00004395 (buf->b_p_sn || buf->b_shortname),
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004396 fname_res,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004397 (char_u *)
Bram Moolenaare60acc12011-05-10 16:41:25 +02004398#if defined(VMS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004399 "_swp",
4400#else
4401 ".swp",
4402#endif
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004403 // Prepend a '.' to the swap file name for the current directory.
Bram Moolenaar48e330a2016-02-23 14:53:34 +01004404 dir_name[0] == '.' && dir_name[1] == NUL);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004405 if (r == NULL) // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00004406 return NULL;
4407
4408 s = get_file_in_dir(r, dir_name);
4409 vim_free(r);
4410 return s;
4411}
4412
4413/*
4414 * Get file name to use for swap file or backup file.
4415 * Use the name of the edited file "fname" and an entry in the 'dir' or 'bdir'
4416 * option "dname".
4417 * - If "dname" is ".", return "fname" (swap file in dir of file).
4418 * - If "dname" starts with "./", insert "dname" in "fname" (swap file
4419 * relative to dir of file).
4420 * - Otherwise, prepend "dname" to the tail of "fname" (swap file in specific
4421 * dir).
4422 *
4423 * The return value is an allocated string and can be NULL.
4424 */
4425 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004426get_file_in_dir(
4427 char_u *fname,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004428 char_u *dname) // don't use "dirname", it is a global for Alpha
Bram Moolenaar071d4272004-06-13 20:20:40 +00004429{
4430 char_u *t;
4431 char_u *tail;
4432 char_u *retval;
4433 int save_char;
4434
4435 tail = gettail(fname);
4436
4437 if (dname[0] == '.' && dname[1] == NUL)
4438 retval = vim_strsave(fname);
4439 else if (dname[0] == '.' && vim_ispathsep(dname[1]))
4440 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004441 if (tail == fname) // no path before file name
Bram Moolenaar071d4272004-06-13 20:20:40 +00004442 retval = concat_fnames(dname + 2, tail, TRUE);
4443 else
4444 {
4445 save_char = *tail;
4446 *tail = NUL;
4447 t = concat_fnames(fname, dname + 2, TRUE);
4448 *tail = save_char;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004449 if (t == NULL) // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00004450 retval = NULL;
4451 else
4452 {
4453 retval = concat_fnames(t, tail, TRUE);
4454 vim_free(t);
4455 }
4456 }
4457 }
4458 else
4459 retval = concat_fnames(dname, tail, TRUE);
4460
Bram Moolenaar4f974752019-02-17 17:44:42 +01004461#ifdef MSWIN
Bram Moolenaar69c35002013-11-04 02:54:12 +01004462 if (retval != NULL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01004463 for (t = gettail(retval); *t != NUL; MB_PTR_ADV(t))
Bram Moolenaar69c35002013-11-04 02:54:12 +01004464 if (*t == ':')
4465 *t = '%';
4466#endif
4467
Bram Moolenaar071d4272004-06-13 20:20:40 +00004468 return retval;
4469}
4470
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004471/*
4472 * Print the ATTENTION message: info about an existing swap file.
4473 */
4474 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004475attention_message(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004476 buf_T *buf, // buffer being edited
4477 char_u *fname) // swap file name
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004478{
Bram Moolenaar8767f522016-07-01 17:17:39 +02004479 stat_T st;
Bram Moolenaar63d25552019-05-10 21:28:38 +02004480 time_t swap_mtime;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004481
4482 ++no_wait_return;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004483 (void)emsg(_("E325: ATTENTION"));
Bram Moolenaar32526b32019-01-19 17:43:09 +01004484 msg_puts(_("\nFound a swap file by the name \""));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004485 msg_home_replace(fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004486 msg_puts("\"\n");
Bram Moolenaar63d25552019-05-10 21:28:38 +02004487 swap_mtime = swapfile_info(fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004488 msg_puts(_("While opening file \""));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004489 msg_outtrans(buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004490 msg_puts("\"\n");
Bram Moolenaard6105cb2018-10-13 19:06:27 +02004491 if (mch_stat((char *)buf->b_fname, &st) == -1)
4492 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004493 msg_puts(_(" CANNOT BE FOUND"));
Bram Moolenaard6105cb2018-10-13 19:06:27 +02004494 }
4495 else
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004496 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004497 msg_puts(_(" dated: "));
Bram Moolenaar63d25552019-05-10 21:28:38 +02004498 msg_puts(get_ctime(st.st_mtime, TRUE));
4499 if (swap_mtime != 0 && st.st_mtime > swap_mtime)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004500 msg_puts(_(" NEWER than swap file!\n"));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004501 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004502 // Some of these messages are long to allow translation to
4503 // other languages.
Bram Moolenaar32526b32019-01-19 17:43:09 +01004504 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"));
4505 msg_puts(_("(2) An edit session for this file crashed.\n"));
4506 msg_puts(_(" If this is the case, use \":recover\" or \"vim -r "));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004507 msg_outtrans(buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004508 msg_puts(_("\"\n to recover the changes (see \":help recovery\").\n"));
4509 msg_puts(_(" If you did this already, delete the swap file \""));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004510 msg_outtrans(fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004511 msg_puts(_("\"\n to avoid this message.\n"));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004512 cmdline_row = msg_row;
4513 --no_wait_return;
4514}
4515
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01004516#if defined(FEAT_EVAL)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004517/*
4518 * Trigger the SwapExists autocommands.
4519 * Returns a value for equivalent to do_dialog() (see below):
4520 * 0: still need to ask for a choice
4521 * 1: open read-only
4522 * 2: edit anyway
4523 * 3: recover
4524 * 4: delete it
4525 * 5: quit
4526 * 6: abort
4527 */
4528 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004529do_swapexists(buf_T *buf, char_u *fname)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004530{
4531 set_vim_var_string(VV_SWAPNAME, fname, -1);
4532 set_vim_var_string(VV_SWAPCHOICE, NULL, -1);
4533
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004534 // Trigger SwapExists autocommands with <afile> set to the file being
4535 // edited. Disallow changing directory here.
Bram Moolenaar12c22ce2009-04-22 13:58:46 +00004536 ++allbuf_lock;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004537 apply_autocmds(EVENT_SWAPEXISTS, buf->b_fname, NULL, FALSE, NULL);
Bram Moolenaar12c22ce2009-04-22 13:58:46 +00004538 --allbuf_lock;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004539
4540 set_vim_var_string(VV_SWAPNAME, NULL, -1);
4541
4542 switch (*get_vim_var_str(VV_SWAPCHOICE))
4543 {
4544 case 'o': return 1;
4545 case 'e': return 2;
4546 case 'r': return 3;
4547 case 'd': return 4;
4548 case 'q': return 5;
4549 case 'a': return 6;
4550 }
4551
4552 return 0;
4553}
4554#endif
4555
Bram Moolenaar071d4272004-06-13 20:20:40 +00004556/*
4557 * Find out what name to use for the swap file for buffer 'buf'.
4558 *
4559 * Several names are tried to find one that does not exist
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004560 * Returns the name in allocated memory or NULL.
Bram Moolenaarf541c362011-10-26 11:44:18 +02004561 * When out of memory "dirp" is set to NULL.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004562 *
4563 * Note: If BASENAMELEN is not correct, you will get error messages for
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004564 * not being able to open the swap or undo file
Bram Moolenaar12c22ce2009-04-22 13:58:46 +00004565 * Note: May trigger SwapExists autocmd, pointers may change!
Bram Moolenaar071d4272004-06-13 20:20:40 +00004566 */
4567 static char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004568findswapname(
4569 buf_T *buf,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004570 char_u **dirp, // pointer to list of directories
4571 char_u *old_fname) // don't give warning for this file name
Bram Moolenaar071d4272004-06-13 20:20:40 +00004572{
4573 char_u *fname;
4574 int n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004575 char_u *dir_name;
4576#ifdef AMIGA
4577 BPTR fh;
4578#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004579 int r;
Bram Moolenaar69c35002013-11-04 02:54:12 +01004580 char_u *buf_fname = buf->b_fname;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004581
Bram Moolenaar48e330a2016-02-23 14:53:34 +01004582#if !defined(UNIX)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004583# define CREATE_DUMMY_FILE
4584 FILE *dummyfd = NULL;
4585
Bram Moolenaar4f974752019-02-17 17:44:42 +01004586# ifdef MSWIN
Bram Moolenaar69c35002013-11-04 02:54:12 +01004587 if (buf_fname != NULL && !mch_isFullName(buf_fname)
4588 && vim_strchr(gettail(buf_fname), ':'))
4589 {
4590 char_u *t;
4591
4592 buf_fname = vim_strsave(buf_fname);
4593 if (buf_fname == NULL)
4594 buf_fname = buf->b_fname;
4595 else
Bram Moolenaar91acfff2017-03-12 19:22:36 +01004596 for (t = gettail(buf_fname); *t != NUL; MB_PTR_ADV(t))
Bram Moolenaar69c35002013-11-04 02:54:12 +01004597 if (*t == ':')
4598 *t = '%';
4599 }
4600# endif
4601
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004602 /*
4603 * If we start editing a new file, e.g. "test.doc", which resides on an
4604 * MSDOS compatible filesystem, it is possible that the file
4605 * "test.doc.swp" which we create will be exactly the same file. To avoid
4606 * this problem we temporarily create "test.doc". Don't do this when the
4607 * check below for a 8.3 file name is used.
4608 */
Bram Moolenaar69c35002013-11-04 02:54:12 +01004609 if (!(buf->b_p_sn || buf->b_shortname) && buf_fname != NULL
4610 && mch_getperm(buf_fname) < 0)
4611 dummyfd = mch_fopen((char *)buf_fname, "w");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004612#endif
4613
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004614 /*
4615 * Isolate a directory name from *dirp and put it in dir_name.
4616 * First allocate some memory to put the directory name in.
4617 */
Bram Moolenaar964b3742019-05-24 18:54:09 +02004618 dir_name = alloc(STRLEN(*dirp) + 1);
Bram Moolenaarf541c362011-10-26 11:44:18 +02004619 if (dir_name == NULL)
4620 *dirp = NULL;
4621 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004622 (void)copy_option_part(dirp, dir_name, 31000, ",");
4623
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004624 /*
4625 * we try different names until we find one that does not exist yet
4626 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004627 if (dir_name == NULL) // out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00004628 fname = NULL;
4629 else
Bram Moolenaar69c35002013-11-04 02:54:12 +01004630 fname = makeswapname(buf_fname, buf->b_ffname, buf, dir_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004631
4632 for (;;)
4633 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004634 if (fname == NULL) // must be out of memory
Bram Moolenaar071d4272004-06-13 20:20:40 +00004635 break;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004636 if ((n = (int)STRLEN(fname)) == 0) // safety check
Bram Moolenaar071d4272004-06-13 20:20:40 +00004637 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01004638 VIM_CLEAR(fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004639 break;
4640 }
Bram Moolenaar48e330a2016-02-23 14:53:34 +01004641#if defined(UNIX)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004642/*
4643 * Some systems have a MS-DOS compatible filesystem that use 8.3 character
4644 * file names. If this is the first try and the swap file name does not fit in
4645 * 8.3, detect if this is the case, set shortname and try again.
4646 */
4647 if (fname[n - 2] == 'w' && fname[n - 1] == 'p'
4648 && !(buf->b_p_sn || buf->b_shortname))
4649 {
4650 char_u *tail;
4651 char_u *fname2;
Bram Moolenaar8767f522016-07-01 17:17:39 +02004652 stat_T s1, s2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004653 int f1, f2;
4654 int created1 = FALSE, created2 = FALSE;
4655 int same = FALSE;
4656
4657 /*
4658 * Check if swapfile name does not fit in 8.3:
4659 * It either contains two dots, is longer than 8 chars, or starts
4660 * with a dot.
4661 */
Bram Moolenaar69c35002013-11-04 02:54:12 +01004662 tail = gettail(buf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004663 if ( vim_strchr(tail, '.') != NULL
4664 || STRLEN(tail) > (size_t)8
4665 || *gettail(fname) == '.')
4666 {
4667 fname2 = alloc(n + 2);
4668 if (fname2 != NULL)
4669 {
4670 STRCPY(fname2, fname);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004671 // if fname == "xx.xx.swp", fname2 = "xx.xx.swx"
4672 // if fname == ".xx.swp", fname2 = ".xx.swpx"
4673 // if fname == "123456789.swp", fname2 = "12345678x.swp"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004674 if (vim_strchr(tail, '.') != NULL)
4675 fname2[n - 1] = 'x';
4676 else if (*gettail(fname) == '.')
4677 {
4678 fname2[n] = 'x';
4679 fname2[n + 1] = NUL;
4680 }
4681 else
4682 fname2[n - 5] += 1;
4683 /*
4684 * may need to create the files to be able to use mch_stat()
4685 */
4686 f1 = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
4687 if (f1 < 0)
4688 {
4689 f1 = mch_open_rw((char *)fname,
4690 O_RDWR|O_CREAT|O_EXCL|O_EXTRA);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004691 created1 = TRUE;
4692 }
4693 if (f1 >= 0)
4694 {
4695 f2 = mch_open((char *)fname2, O_RDONLY | O_EXTRA, 0);
4696 if (f2 < 0)
4697 {
4698 f2 = mch_open_rw((char *)fname2,
4699 O_RDWR|O_CREAT|O_EXCL|O_EXTRA);
4700 created2 = TRUE;
4701 }
4702 if (f2 >= 0)
4703 {
4704 /*
4705 * Both files exist now. If mch_stat() returns the
4706 * same device and inode they are the same file.
4707 */
4708 if (mch_fstat(f1, &s1) != -1
4709 && mch_fstat(f2, &s2) != -1
4710 && s1.st_dev == s2.st_dev
4711 && s1.st_ino == s2.st_ino)
4712 same = TRUE;
4713 close(f2);
4714 if (created2)
4715 mch_remove(fname2);
4716 }
4717 close(f1);
4718 if (created1)
4719 mch_remove(fname);
4720 }
4721 vim_free(fname2);
4722 if (same)
4723 {
4724 buf->b_shortname = TRUE;
4725 vim_free(fname);
Bram Moolenaar69c35002013-11-04 02:54:12 +01004726 fname = makeswapname(buf_fname, buf->b_ffname,
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004727 buf, dir_name);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004728 continue; // try again with b_shortname set
Bram Moolenaar071d4272004-06-13 20:20:40 +00004729 }
4730 }
4731 }
4732 }
4733#endif
4734 /*
4735 * check if the swapfile already exists
4736 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004737 if (mch_getperm(fname) < 0) // it does not exist
Bram Moolenaar071d4272004-06-13 20:20:40 +00004738 {
4739#ifdef HAVE_LSTAT
Bram Moolenaar8767f522016-07-01 17:17:39 +02004740 stat_T sb;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004741
4742 /*
4743 * Extra security check: When a swap file is a symbolic link, this
4744 * is most likely a symlink attack.
4745 */
4746 if (mch_lstat((char *)fname, &sb) < 0)
4747#else
4748# ifdef AMIGA
4749 fh = Open((UBYTE *)fname, (long)MODE_NEWFILE);
4750 /*
4751 * on the Amiga mch_getperm() will return -1 when the file exists
4752 * but is being used by another program. This happens if you edit
4753 * a file twice.
4754 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004755 if (fh != (BPTR)NULL) // can open file, OK
Bram Moolenaar071d4272004-06-13 20:20:40 +00004756 {
4757 Close(fh);
4758 mch_remove(fname);
4759 break;
4760 }
4761 if (IoErr() != ERROR_OBJECT_IN_USE
4762 && IoErr() != ERROR_OBJECT_EXISTS)
4763# endif
4764#endif
4765 break;
4766 }
4767
4768 /*
4769 * A file name equal to old_fname is OK to use.
4770 */
4771 if (old_fname != NULL && fnamecmp(fname, old_fname) == 0)
4772 break;
4773
4774 /*
4775 * get here when file already exists
4776 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004777 if (fname[n - 2] == 'w' && fname[n - 1] == 'p') // first try
Bram Moolenaar071d4272004-06-13 20:20:40 +00004778 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00004779 /*
4780 * on MS-DOS compatible filesystems (e.g. messydos) file.doc.swp
4781 * and file.doc are the same file. To guess if this problem is
4782 * present try if file.doc.swx exists. If it does, we set
4783 * buf->b_shortname and try file_doc.swp (dots replaced by
4784 * underscores for this file), and try again. If it doesn't we
4785 * assume that "file.doc.swp" already exists.
4786 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004787 if (!(buf->b_p_sn || buf->b_shortname)) // not tried yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00004788 {
4789 fname[n - 1] = 'x';
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004790 r = mch_getperm(fname); // try "file.swx"
Bram Moolenaar071d4272004-06-13 20:20:40 +00004791 fname[n - 1] = 'p';
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004792 if (r >= 0) // "file.swx" seems to exist
Bram Moolenaar071d4272004-06-13 20:20:40 +00004793 {
4794 buf->b_shortname = TRUE;
4795 vim_free(fname);
Bram Moolenaar69c35002013-11-04 02:54:12 +01004796 fname = makeswapname(buf_fname, buf->b_ffname,
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004797 buf, dir_name);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004798 continue; // try again with '.' replaced with '_'
Bram Moolenaar071d4272004-06-13 20:20:40 +00004799 }
4800 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004801 /*
4802 * If we get here the ".swp" file really exists.
4803 * Give an error message, unless recovering, no file name, we are
4804 * viewing a help file or when the path of the file is different
4805 * (happens when all .swp files are in one directory).
4806 */
Bram Moolenaar69c35002013-11-04 02:54:12 +01004807 if (!recoverymode && buf_fname != NULL
Bram Moolenaar2debf1c2019-08-04 20:44:19 +02004808 && !buf->b_help
4809 && !(buf->b_flags & (BF_DUMMY | BF_NO_SEA)))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004810 {
4811 int fd;
4812 struct block0 b0;
4813 int differ = FALSE;
4814
4815 /*
4816 * Try to read block 0 from the swap file to get the original
4817 * file name (and inode number).
4818 */
4819 fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
4820 if (fd >= 0)
4821 {
Bram Moolenaar540fc6f2010-12-17 16:27:16 +01004822 if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004823 {
4824 /*
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004825 * If the swapfile has the same directory as the
4826 * buffer don't compare the directory names, they can
4827 * have a different mountpoint.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004828 */
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004829 if (b0.b0_flags & B0_SAME_DIR)
4830 {
4831 if (fnamecmp(gettail(buf->b_ffname),
4832 gettail(b0.b0_fname)) != 0
4833 || !same_directory(fname, buf->b_ffname))
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004834 {
4835#ifdef CHECK_INODE
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004836 // Symlinks may point to the same file even
4837 // when the name differs, need to check the
4838 // inode too.
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004839 expand_env(b0.b0_fname, NameBuff, MAXPATHL);
4840 if (fnamecmp_ino(buf->b_ffname, NameBuff,
4841 char_to_long(b0.b0_ino)))
4842#endif
4843 differ = TRUE;
4844 }
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004845 }
4846 else
4847 {
4848 /*
4849 * The name in the swap file may be
4850 * "~user/path/file". Expand it first.
4851 */
4852 expand_env(b0.b0_fname, NameBuff, MAXPATHL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004853#ifdef CHECK_INODE
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004854 if (fnamecmp_ino(buf->b_ffname, NameBuff,
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004855 char_to_long(b0.b0_ino)))
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004856 differ = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004857#else
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004858 if (fnamecmp(NameBuff, buf->b_ffname) != 0)
4859 differ = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004860#endif
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004861 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004862 }
4863 close(fd);
4864 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004865
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004866 // give the ATTENTION message when there is an old swap file
4867 // for the current file, and the buffer was not recovered.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004868 if (differ == FALSE && !(curbuf->b_flags & BF_RECOVERED)
4869 && vim_strchr(p_shm, SHM_ATTENTION) == NULL)
4870 {
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004871 int choice = 0;
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02004872 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004873#ifdef CREATE_DUMMY_FILE
4874 int did_use_dummy = FALSE;
4875
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004876 // Avoid getting a warning for the file being created
4877 // outside of Vim, it was created at the start of this
4878 // function. Delete the file now, because Vim might exit
4879 // here if the window is closed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004880 if (dummyfd != NULL)
4881 {
4882 fclose(dummyfd);
4883 dummyfd = NULL;
Bram Moolenaar69c35002013-11-04 02:54:12 +01004884 mch_remove(buf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004885 did_use_dummy = TRUE;
4886 }
4887#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004888
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02004889#ifdef HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar071d4272004-06-13 20:20:40 +00004890 process_still_running = FALSE;
4891#endif
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02004892 // It's safe to delete the swap file if all these are true:
4893 // - the edited file exists
4894 // - the swap file has no changes and looks OK
4895 if (mch_stat((char *)buf->b_fname, &st) == 0
4896 && swapfile_unchanged(fname))
4897 {
4898 choice = 4;
4899 if (p_verbose > 0)
4900 verb_msg(_("Found a swap file that is not useful, deleting it"));
4901 }
4902
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01004903#if defined(FEAT_EVAL)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004904 /*
4905 * If there is an SwapExists autocommand and we can handle
4906 * the response, trigger it. It may return 0 to ask the
4907 * user anyway.
4908 */
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02004909 if (choice == 0
4910 && swap_exists_action != SEA_NONE
Bram Moolenaar69c35002013-11-04 02:54:12 +01004911 && has_autocmd(EVENT_SWAPEXISTS, buf_fname, buf))
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004912 choice = do_swapexists(buf, fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004913
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004914 if (choice == 0)
4915#endif
4916 {
4917#ifdef FEAT_GUI
Bram Moolenaar798184c2018-10-07 20:48:39 +02004918 // If we are supposed to start the GUI but it wasn't
4919 // completely started yet, start it now. This makes
4920 // the messages displayed in the Vim window when
4921 // loading a session from the .gvimrc file.
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004922 if (gui.starting && !gui.in_use)
Bram Moolenaarafde13b2019-04-28 19:46:49 +02004923 gui_start(NULL);
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004924#endif
Bram Moolenaar798184c2018-10-07 20:48:39 +02004925 // Show info about the existing swap file.
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004926 attention_message(buf, fname);
4927
Bram Moolenaar798184c2018-10-07 20:48:39 +02004928 // We don't want a 'q' typed at the more-prompt
4929 // interrupt loading a file.
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004930 got_int = FALSE;
Bram Moolenaar798184c2018-10-07 20:48:39 +02004931
4932 // If vimrc has "simalt ~x" we don't want it to
4933 // interfere with the prompt here.
Bram Moolenaar6a2633b2018-10-07 23:16:36 +02004934 flush_buffers(FLUSH_TYPEAHEAD);
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004935 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004936
4937#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004938 if (swap_exists_action != SEA_NONE && choice == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004939 {
4940 char_u *name;
4941
Bram Moolenaar964b3742019-05-24 18:54:09 +02004942 name = alloc(STRLEN(fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004943 + STRLEN(_("Swap file \""))
Bram Moolenaar964b3742019-05-24 18:54:09 +02004944 + STRLEN(_("\" already exists!")) + 5);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004945 if (name != NULL)
4946 {
4947 STRCPY(name, _("Swap file \""));
4948 home_replace(NULL, fname, name + STRLEN(name),
4949 1000, TRUE);
4950 STRCAT(name, _("\" already exists!"));
4951 }
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004952 choice = do_dialog(VIM_WARNING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004953 (char_u *)_("VIM - ATTENTION"),
4954 name == NULL
4955 ? (char_u *)_("Swap file already exists!")
4956 : name,
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02004957# ifdef HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar071d4272004-06-13 20:20:40 +00004958 process_still_running
4959 ? (char_u *)_("&Open Read-Only\n&Edit anyway\n&Recover\n&Quit\n&Abort") :
4960# endif
Bram Moolenaard2c340a2011-01-17 20:08:11 +01004961 (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 +00004962
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02004963# ifdef HAVE_PROCESS_STILL_RUNNING
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004964 if (process_still_running && choice >= 4)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004965 choice++; // Skip missing "Delete it" button
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004966# endif
4967 vim_free(name);
4968
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004969 // pretend screen didn't scroll, need redraw anyway
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004970 msg_scrolled = 0;
4971 redraw_all_later(NOT_VALID);
4972 }
4973#endif
4974
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004975 if (choice > 0)
4976 {
4977 switch (choice)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004978 {
4979 case 1:
4980 buf->b_p_ro = TRUE;
4981 break;
4982 case 2:
4983 break;
4984 case 3:
4985 swap_exists_action = SEA_RECOVER;
4986 break;
4987 case 4:
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004988 mch_remove(fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004989 break;
4990 case 5:
4991 swap_exists_action = SEA_QUIT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004992 break;
4993 case 6:
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004994 swap_exists_action = SEA_QUIT;
4995 got_int = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004996 break;
4997 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004998
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01004999 // If the file was deleted this fname can be used.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005000 if (mch_getperm(fname) < 0)
5001 break;
5002 }
5003 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005004 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01005005 msg_puts("\n");
Bram Moolenaar4770d092006-01-12 23:22:24 +00005006 if (msg_silent == 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005007 // call wait_return() later
Bram Moolenaar4770d092006-01-12 23:22:24 +00005008 need_wait_return = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005009 }
5010
5011#ifdef CREATE_DUMMY_FILE
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005012 // Going to try another name, need the dummy file again.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005013 if (did_use_dummy)
Bram Moolenaar69c35002013-11-04 02:54:12 +01005014 dummyfd = mch_fopen((char *)buf_fname, "w");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005015#endif
5016 }
5017 }
5018 }
5019
5020 /*
5021 * Change the ".swp" extension to find another file that can be used.
5022 * First decrement the last char: ".swo", ".swn", etc.
5023 * If that still isn't enough decrement the last but one char: ".svz"
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00005024 * Can happen when editing many "No Name" buffers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005025 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005026 if (fname[n - 1] == 'a') // ".s?a"
Bram Moolenaar071d4272004-06-13 20:20:40 +00005027 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005028 if (fname[n - 2] == 'a') // ".saa": tried enough, give up
Bram Moolenaar071d4272004-06-13 20:20:40 +00005029 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005030 emsg(_("E326: Too many swap files found"));
Bram Moolenaard23a8232018-02-10 18:45:26 +01005031 VIM_CLEAR(fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005032 break;
5033 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005034 --fname[n - 2]; // ".svz", ".suz", etc.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005035 fname[n - 1] = 'z' + 1;
5036 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005037 --fname[n - 1]; // ".swo", ".swn", etc.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005038 }
5039
5040 vim_free(dir_name);
5041#ifdef CREATE_DUMMY_FILE
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005042 if (dummyfd != NULL) // file has been created temporarily
Bram Moolenaar071d4272004-06-13 20:20:40 +00005043 {
5044 fclose(dummyfd);
Bram Moolenaar69c35002013-11-04 02:54:12 +01005045 mch_remove(buf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005046 }
5047#endif
Bram Moolenaar4f974752019-02-17 17:44:42 +01005048#ifdef MSWIN
Bram Moolenaar69c35002013-11-04 02:54:12 +01005049 if (buf_fname != buf->b_fname)
5050 vim_free(buf_fname);
5051#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005052 return fname;
5053}
5054
5055 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005056b0_magic_wrong(ZERO_BL *b0p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005057{
5058 return (b0p->b0_magic_long != (long)B0_MAGIC_LONG
5059 || b0p->b0_magic_int != (int)B0_MAGIC_INT
5060 || b0p->b0_magic_short != (short)B0_MAGIC_SHORT
5061 || b0p->b0_magic_char != B0_MAGIC_CHAR);
5062}
5063
5064#ifdef CHECK_INODE
5065/*
5066 * Compare current file name with file name from swap file.
5067 * Try to use inode numbers when possible.
5068 * Return non-zero when files are different.
5069 *
5070 * When comparing file names a few things have to be taken into consideration:
5071 * - When working over a network the full path of a file depends on the host.
5072 * We check the inode number if possible. It is not 100% reliable though,
5073 * because the device number cannot be used over a network.
5074 * - When a file does not exist yet (editing a new file) there is no inode
5075 * number.
5076 * - The file name in a swap file may not be valid on the current host. The
5077 * "~user" form is used whenever possible to avoid this.
5078 *
5079 * This is getting complicated, let's make a table:
5080 *
5081 * ino_c ino_s fname_c fname_s differ =
5082 *
5083 * both files exist -> compare inode numbers:
5084 * != 0 != 0 X X ino_c != ino_s
5085 *
5086 * inode number(s) unknown, file names available -> compare file names
5087 * == 0 X OK OK fname_c != fname_s
5088 * X == 0 OK OK fname_c != fname_s
5089 *
5090 * current file doesn't exist, file for swap file exist, file name(s) not
5091 * available -> probably different
5092 * == 0 != 0 FAIL X TRUE
5093 * == 0 != 0 X FAIL TRUE
5094 *
5095 * current file exists, inode for swap unknown, file name(s) not
5096 * available -> probably different
5097 * != 0 == 0 FAIL X TRUE
5098 * != 0 == 0 X FAIL TRUE
5099 *
5100 * current file doesn't exist, inode for swap unknown, one file name not
5101 * available -> probably different
5102 * == 0 == 0 FAIL OK TRUE
5103 * == 0 == 0 OK FAIL TRUE
5104 *
5105 * current file doesn't exist, inode for swap unknown, both file names not
Bram Moolenaar8c3169c2018-05-12 17:04:12 +02005106 * available -> compare file names
5107 * == 0 == 0 FAIL FAIL fname_c != fname_s
Bram Moolenaar071d4272004-06-13 20:20:40 +00005108 *
5109 * Note that when the ino_t is 64 bits, only the last 32 will be used. This
5110 * can't be changed without making the block 0 incompatible with 32 bit
5111 * versions.
5112 */
5113
5114 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005115fnamecmp_ino(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005116 char_u *fname_c, // current file name
5117 char_u *fname_s, // file name from swap file
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005118 long ino_block0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005119{
Bram Moolenaar8767f522016-07-01 17:17:39 +02005120 stat_T st;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005121 ino_t ino_c = 0; // ino of current file
5122 ino_t ino_s; // ino of file from swap file
5123 char_u buf_c[MAXPATHL]; // full path of fname_c
5124 char_u buf_s[MAXPATHL]; // full path of fname_s
5125 int retval_c; // flag: buf_c valid
5126 int retval_s; // flag: buf_s valid
Bram Moolenaar071d4272004-06-13 20:20:40 +00005127
5128 if (mch_stat((char *)fname_c, &st) == 0)
5129 ino_c = (ino_t)st.st_ino;
5130
5131 /*
5132 * First we try to get the inode from the file name, because the inode in
5133 * the swap file may be outdated. If that fails (e.g. this path is not
5134 * valid on this machine), use the inode from block 0.
5135 */
5136 if (mch_stat((char *)fname_s, &st) == 0)
5137 ino_s = (ino_t)st.st_ino;
5138 else
5139 ino_s = (ino_t)ino_block0;
5140
5141 if (ino_c && ino_s)
5142 return (ino_c != ino_s);
5143
5144 /*
5145 * One of the inode numbers is unknown, try a forced vim_FullName() and
5146 * compare the file names.
5147 */
5148 retval_c = vim_FullName(fname_c, buf_c, MAXPATHL, TRUE);
5149 retval_s = vim_FullName(fname_s, buf_s, MAXPATHL, TRUE);
5150 if (retval_c == OK && retval_s == OK)
Bram Moolenaar8c3169c2018-05-12 17:04:12 +02005151 return STRCMP(buf_c, buf_s) != 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005152
5153 /*
5154 * Can't compare inodes or file names, guess that the files are different,
Bram Moolenaar8c3169c2018-05-12 17:04:12 +02005155 * unless both appear not to exist at all, then compare with the file name
5156 * in the swap file.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005157 */
5158 if (ino_s == 0 && ino_c == 0 && retval_c == FAIL && retval_s == FAIL)
Bram Moolenaar8c3169c2018-05-12 17:04:12 +02005159 return STRCMP(fname_c, fname_s) != 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005160 return TRUE;
5161}
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005162#endif // CHECK_INODE
Bram Moolenaar071d4272004-06-13 20:20:40 +00005163
5164/*
5165 * Move a long integer into a four byte character array.
5166 * Used for machine independency in block zero.
5167 */
5168 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005169long_to_char(long n, char_u *s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005170{
5171 s[0] = (char_u)(n & 0xff);
5172 n = (unsigned)n >> 8;
5173 s[1] = (char_u)(n & 0xff);
5174 n = (unsigned)n >> 8;
5175 s[2] = (char_u)(n & 0xff);
5176 n = (unsigned)n >> 8;
5177 s[3] = (char_u)(n & 0xff);
5178}
5179
5180 static long
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005181char_to_long(char_u *s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005182{
5183 long retval;
5184
5185 retval = s[3];
5186 retval <<= 8;
5187 retval |= s[2];
5188 retval <<= 8;
5189 retval |= s[1];
5190 retval <<= 8;
5191 retval |= s[0];
5192
5193 return retval;
5194}
5195
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005196/*
5197 * Set the flags in the first block of the swap file:
5198 * - file is modified or not: buf->b_changed
5199 * - 'fileformat'
5200 * - 'fileencoding'
5201 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005202 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005203ml_setflags(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005204{
5205 bhdr_T *hp;
5206 ZERO_BL *b0p;
5207
5208 if (!buf->b_ml.ml_mfp)
5209 return;
5210 for (hp = buf->b_ml.ml_mfp->mf_used_last; hp != NULL; hp = hp->bh_prev)
5211 {
5212 if (hp->bh_bnum == 0)
5213 {
5214 b0p = (ZERO_BL *)(hp->bh_data);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005215 b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0;
5216 b0p->b0_flags = (b0p->b0_flags & ~B0_FF_MASK)
5217 | (get_fileformat(buf) + 1);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005218 add_b0_fenc(b0p, buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005219 hp->bh_flags |= BH_DIRTY;
5220 mf_sync(buf->b_ml.ml_mfp, MFS_ZERO);
5221 break;
5222 }
5223 }
5224}
5225
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005226#if defined(FEAT_CRYPT) || defined(PROTO)
5227/*
5228 * If "data" points to a data block encrypt the text in it and return a copy
5229 * in allocated memory. Return NULL when out of memory.
5230 * Otherwise return "data".
5231 */
5232 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005233ml_encrypt_data(
5234 memfile_T *mfp,
5235 char_u *data,
Bram Moolenaar8767f522016-07-01 17:17:39 +02005236 off_T offset,
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005237 unsigned size)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005238{
5239 DATA_BL *dp = (DATA_BL *)data;
5240 char_u *head_end;
5241 char_u *text_start;
5242 char_u *new_data;
5243 int text_len;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005244 cryptstate_T *state;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005245
5246 if (dp->db_id != DATA_ID)
5247 return data;
5248
Bram Moolenaarbc563362015-06-09 18:35:25 +02005249 state = ml_crypt_prepare(mfp, offset, FALSE);
5250 if (state == NULL)
5251 return data;
5252
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005253 new_data = alloc(size);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005254 if (new_data == NULL)
5255 return NULL;
5256 head_end = (char_u *)(&dp->db_index[dp->db_line_count]);
5257 text_start = (char_u *)dp + dp->db_txt_start;
5258 text_len = size - dp->db_txt_start;
5259
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005260 // Copy the header and the text.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005261 mch_memmove(new_data, dp, head_end - (char_u *)dp);
5262
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005263 // Encrypt the text.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005264 crypt_encode(state, text_start, text_len, new_data + dp->db_txt_start);
5265 crypt_free_state(state);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005266
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005267 // Clear the gap.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005268 if (head_end < text_start)
5269 vim_memset(new_data + (head_end - data), 0, text_start - head_end);
5270
5271 return new_data;
5272}
5273
5274/*
Bram Moolenaarbc563362015-06-09 18:35:25 +02005275 * Decrypt the text in "data" if it points to an encrypted data block.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005276 */
5277 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005278ml_decrypt_data(
5279 memfile_T *mfp,
5280 char_u *data,
Bram Moolenaar8767f522016-07-01 17:17:39 +02005281 off_T offset,
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005282 unsigned size)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005283{
5284 DATA_BL *dp = (DATA_BL *)data;
5285 char_u *head_end;
5286 char_u *text_start;
5287 int text_len;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005288 cryptstate_T *state;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005289
5290 if (dp->db_id == DATA_ID)
5291 {
5292 head_end = (char_u *)(&dp->db_index[dp->db_line_count]);
5293 text_start = (char_u *)dp + dp->db_txt_start;
5294 text_len = dp->db_txt_end - dp->db_txt_start;
5295
5296 if (head_end > text_start || dp->db_txt_start > size
5297 || dp->db_txt_end > size)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005298 return; // data was messed up
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005299
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005300 state = ml_crypt_prepare(mfp, offset, TRUE);
Bram Moolenaarbc563362015-06-09 18:35:25 +02005301 if (state != NULL)
5302 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005303 // Decrypt the text in place.
Bram Moolenaarbc563362015-06-09 18:35:25 +02005304 crypt_decode_inplace(state, text_start, text_len);
5305 crypt_free_state(state);
5306 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005307 }
5308}
5309
5310/*
5311 * Prepare for encryption/decryption, using the key, seed and offset.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005312 * Return an allocated cryptstate_T *.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005313 */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005314 static cryptstate_T *
Bram Moolenaar8767f522016-07-01 17:17:39 +02005315ml_crypt_prepare(memfile_T *mfp, off_T offset, int reading)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005316{
5317 buf_T *buf = mfp->mf_buffer;
5318 char_u salt[50];
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005319 int method_nr;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005320 char_u *key;
5321 char_u *seed;
5322
5323 if (reading && mfp->mf_old_key != NULL)
5324 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005325 // Reading back blocks with the previous key/method/seed.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005326 method_nr = mfp->mf_old_cm;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005327 key = mfp->mf_old_key;
5328 seed = mfp->mf_old_seed;
5329 }
5330 else
5331 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005332 method_nr = crypt_get_method_nr(buf);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005333 key = buf->b_p_key;
5334 seed = mfp->mf_seed;
5335 }
Bram Moolenaarbc563362015-06-09 18:35:25 +02005336 if (*key == NUL)
5337 return NULL;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005338
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005339 if (method_nr == CRYPT_M_ZIP)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005340 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005341 // For PKzip: Append the offset to the key, so that we use a different
5342 // key for every block.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005343 vim_snprintf((char *)salt, sizeof(salt), "%s%ld", key, (long)offset);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005344 return crypt_create(method_nr, salt, NULL, 0, NULL, 0);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005345 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005346
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005347 // Using blowfish or better: add salt and seed. We use the byte offset
5348 // of the block for the salt.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005349 vim_snprintf((char *)salt, sizeof(salt), "%ld", (long)offset);
5350 return crypt_create(method_nr, key, salt, (int)STRLEN(salt),
5351 seed, MF_SEED_LEN);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005352}
5353
5354#endif
5355
5356
Bram Moolenaar071d4272004-06-13 20:20:40 +00005357#if defined(FEAT_BYTEOFF) || defined(PROTO)
5358
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005359#define MLCS_MAXL 800 // max no of lines in chunk
5360#define MLCS_MINL 400 // should be half of MLCS_MAXL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005361
5362/*
Bram Moolenaar0ad014c2010-07-25 14:00:46 +02005363 * Keep information for finding byte offset of a line, updtype may be one of:
Bram Moolenaar071d4272004-06-13 20:20:40 +00005364 * ML_CHNK_ADDLINE: Add len to parent chunk, possibly splitting it
5365 * Careful: ML_CHNK_ADDLINE may cause ml_find_line() to be called.
5366 * ML_CHNK_DELLINE: Subtract len from parent chunk, possibly deleting it
5367 * ML_CHNK_UPDLINE: Add len to parent chunk, as a signed entity.
5368 */
5369 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005370ml_updatechunk(
5371 buf_T *buf,
5372 linenr_T line,
5373 long len,
5374 int updtype)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005375{
5376 static buf_T *ml_upd_lastbuf = NULL;
5377 static linenr_T ml_upd_lastline;
5378 static linenr_T ml_upd_lastcurline;
5379 static int ml_upd_lastcurix;
5380
5381 linenr_T curline = ml_upd_lastcurline;
5382 int curix = ml_upd_lastcurix;
5383 long size;
5384 chunksize_T *curchnk;
5385 int rest;
5386 bhdr_T *hp;
5387 DATA_BL *dp;
5388
5389 if (buf->b_ml.ml_usedchunks == -1 || len == 0)
5390 return;
5391 if (buf->b_ml.ml_chunksize == NULL)
5392 {
Bram Moolenaarc799fe22019-05-28 23:08:19 +02005393 buf->b_ml.ml_chunksize = ALLOC_MULT(chunksize_T, 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005394 if (buf->b_ml.ml_chunksize == NULL)
5395 {
5396 buf->b_ml.ml_usedchunks = -1;
5397 return;
5398 }
5399 buf->b_ml.ml_numchunks = 100;
5400 buf->b_ml.ml_usedchunks = 1;
5401 buf->b_ml.ml_chunksize[0].mlcs_numlines = 1;
5402 buf->b_ml.ml_chunksize[0].mlcs_totalsize = 1;
5403 }
5404
5405 if (updtype == ML_CHNK_UPDLINE && buf->b_ml.ml_line_count == 1)
5406 {
5407 /*
5408 * First line in empty buffer from ml_flush_line() -- reset
5409 */
5410 buf->b_ml.ml_usedchunks = 1;
5411 buf->b_ml.ml_chunksize[0].mlcs_numlines = 1;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01005412 buf->b_ml.ml_chunksize[0].mlcs_totalsize = (long)buf->b_ml.ml_line_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005413 return;
5414 }
5415
5416 /*
5417 * Find chunk that our line belongs to, curline will be at start of the
5418 * chunk.
5419 */
5420 if (buf != ml_upd_lastbuf || line != ml_upd_lastline + 1
5421 || updtype != ML_CHNK_ADDLINE)
5422 {
5423 for (curline = 1, curix = 0;
5424 curix < buf->b_ml.ml_usedchunks - 1
5425 && line >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines;
5426 curix++)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005427 curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005428 }
Bram Moolenaara9a8e042018-10-30 22:15:55 +01005429 else if (curix < buf->b_ml.ml_usedchunks - 1
5430 && line >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005431 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005432 // Adjust cached curix & curline
Bram Moolenaar071d4272004-06-13 20:20:40 +00005433 curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
5434 curix++;
5435 }
5436 curchnk = buf->b_ml.ml_chunksize + curix;
5437
5438 if (updtype == ML_CHNK_DELLINE)
Bram Moolenaar5a6404c2006-11-01 17:12:57 +00005439 len = -len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005440 curchnk->mlcs_totalsize += len;
5441 if (updtype == ML_CHNK_ADDLINE)
5442 {
5443 curchnk->mlcs_numlines++;
5444
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005445 // May resize here so we don't have to do it in both cases below
Bram Moolenaar071d4272004-06-13 20:20:40 +00005446 if (buf->b_ml.ml_usedchunks + 1 >= buf->b_ml.ml_numchunks)
5447 {
Bram Moolenaar9abd5c62015-02-10 18:34:01 +01005448 chunksize_T *t_chunksize = buf->b_ml.ml_chunksize;
5449
Bram Moolenaar071d4272004-06-13 20:20:40 +00005450 buf->b_ml.ml_numchunks = buf->b_ml.ml_numchunks * 3 / 2;
5451 buf->b_ml.ml_chunksize = (chunksize_T *)
5452 vim_realloc(buf->b_ml.ml_chunksize,
5453 sizeof(chunksize_T) * buf->b_ml.ml_numchunks);
5454 if (buf->b_ml.ml_chunksize == NULL)
5455 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005456 // Hmmmm, Give up on offset for this buffer
Bram Moolenaar9abd5c62015-02-10 18:34:01 +01005457 vim_free(t_chunksize);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005458 buf->b_ml.ml_usedchunks = -1;
5459 return;
5460 }
5461 }
5462
5463 if (buf->b_ml.ml_chunksize[curix].mlcs_numlines >= MLCS_MAXL)
5464 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005465 int count; // number of entries in block
Bram Moolenaar071d4272004-06-13 20:20:40 +00005466 int idx;
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005467 int end_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005468 int text_end;
5469 int linecnt;
5470
5471 mch_memmove(buf->b_ml.ml_chunksize + curix + 1,
5472 buf->b_ml.ml_chunksize + curix,
5473 (buf->b_ml.ml_usedchunks - curix) *
5474 sizeof(chunksize_T));
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005475 // Compute length of first half of lines in the split chunk
Bram Moolenaar071d4272004-06-13 20:20:40 +00005476 size = 0;
5477 linecnt = 0;
5478 while (curline < buf->b_ml.ml_line_count
5479 && linecnt < MLCS_MINL)
5480 {
5481 if ((hp = ml_find_line(buf, curline, ML_FIND)) == NULL)
5482 {
5483 buf->b_ml.ml_usedchunks = -1;
5484 return;
5485 }
5486 dp = (DATA_BL *)(hp->bh_data);
5487 count = (long)(buf->b_ml.ml_locked_high) -
5488 (long)(buf->b_ml.ml_locked_low) + 1;
5489 idx = curline - buf->b_ml.ml_locked_low;
5490 curline = buf->b_ml.ml_locked_high + 1;
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005491
5492 // compute index of last line to use in this MEMLINE
Bram Moolenaar071d4272004-06-13 20:20:40 +00005493 rest = count - idx;
5494 if (linecnt + rest > MLCS_MINL)
5495 {
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005496 end_idx = idx + MLCS_MINL - linecnt - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005497 linecnt = MLCS_MINL;
5498 }
5499 else
5500 {
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005501 end_idx = count - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005502 linecnt += rest;
5503 }
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01005504#ifdef FEAT_PROP_POPUP
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005505 if (buf->b_has_textprop)
5506 {
5507 int i;
5508
5509 // We cannot use the text pointers to get the text length,
5510 // the text prop info would also be counted. Go over the
5511 // lines.
5512 for (i = end_idx; i < idx; ++i)
Bram Moolenaar4b7214e2019-01-03 21:55:32 +01005513 size += (int)STRLEN((char_u *)dp + (dp->db_index[i] & DB_INDEX_MASK)) + 1;
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005514 }
5515 else
5516#endif
5517 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005518 if (idx == 0)// first line in block, text at the end
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005519 text_end = dp->db_txt_end;
5520 else
5521 text_end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK);
5522 size += text_end - ((dp->db_index[end_idx]) & DB_INDEX_MASK);
5523 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005524 }
5525 buf->b_ml.ml_chunksize[curix].mlcs_numlines = linecnt;
5526 buf->b_ml.ml_chunksize[curix + 1].mlcs_numlines -= linecnt;
5527 buf->b_ml.ml_chunksize[curix].mlcs_totalsize = size;
5528 buf->b_ml.ml_chunksize[curix + 1].mlcs_totalsize -= size;
5529 buf->b_ml.ml_usedchunks++;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005530 ml_upd_lastbuf = NULL; // Force recalc of curix & curline
Bram Moolenaar071d4272004-06-13 20:20:40 +00005531 return;
5532 }
5533 else if (buf->b_ml.ml_chunksize[curix].mlcs_numlines >= MLCS_MINL
5534 && curix == buf->b_ml.ml_usedchunks - 1
5535 && buf->b_ml.ml_line_count - line <= 1)
5536 {
5537 /*
5538 * We are in the last chunk and it is cheap to crate a new one
5539 * after this. Do it now to avoid the loop above later on
5540 */
5541 curchnk = buf->b_ml.ml_chunksize + curix + 1;
5542 buf->b_ml.ml_usedchunks++;
5543 if (line == buf->b_ml.ml_line_count)
5544 {
5545 curchnk->mlcs_numlines = 0;
5546 curchnk->mlcs_totalsize = 0;
5547 }
5548 else
5549 {
5550 /*
5551 * Line is just prior to last, move count for last
5552 * This is the common case when loading a new file
5553 */
5554 hp = ml_find_line(buf, buf->b_ml.ml_line_count, ML_FIND);
5555 if (hp == NULL)
5556 {
5557 buf->b_ml.ml_usedchunks = -1;
5558 return;
5559 }
5560 dp = (DATA_BL *)(hp->bh_data);
5561 if (dp->db_line_count == 1)
5562 rest = dp->db_txt_end - dp->db_txt_start;
5563 else
5564 rest =
5565 ((dp->db_index[dp->db_line_count - 2]) & DB_INDEX_MASK)
5566 - dp->db_txt_start;
5567 curchnk->mlcs_totalsize = rest;
5568 curchnk->mlcs_numlines = 1;
5569 curchnk[-1].mlcs_totalsize -= rest;
5570 curchnk[-1].mlcs_numlines -= 1;
5571 }
5572 }
5573 }
5574 else if (updtype == ML_CHNK_DELLINE)
5575 {
5576 curchnk->mlcs_numlines--;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005577 ml_upd_lastbuf = NULL; // Force recalc of curix & curline
Bram Moolenaar071d4272004-06-13 20:20:40 +00005578 if (curix < (buf->b_ml.ml_usedchunks - 1)
5579 && (curchnk->mlcs_numlines + curchnk[1].mlcs_numlines)
5580 <= MLCS_MINL)
5581 {
5582 curix++;
5583 curchnk = buf->b_ml.ml_chunksize + curix;
5584 }
5585 else if (curix == 0 && curchnk->mlcs_numlines <= 0)
5586 {
5587 buf->b_ml.ml_usedchunks--;
5588 mch_memmove(buf->b_ml.ml_chunksize, buf->b_ml.ml_chunksize + 1,
5589 buf->b_ml.ml_usedchunks * sizeof(chunksize_T));
5590 return;
5591 }
5592 else if (curix == 0 || (curchnk->mlcs_numlines > 10
5593 && (curchnk->mlcs_numlines + curchnk[-1].mlcs_numlines)
5594 > MLCS_MINL))
5595 {
5596 return;
5597 }
5598
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005599 // Collapse chunks
Bram Moolenaar071d4272004-06-13 20:20:40 +00005600 curchnk[-1].mlcs_numlines += curchnk->mlcs_numlines;
5601 curchnk[-1].mlcs_totalsize += curchnk->mlcs_totalsize;
5602 buf->b_ml.ml_usedchunks--;
5603 if (curix < buf->b_ml.ml_usedchunks)
5604 {
5605 mch_memmove(buf->b_ml.ml_chunksize + curix,
5606 buf->b_ml.ml_chunksize + curix + 1,
5607 (buf->b_ml.ml_usedchunks - curix) *
5608 sizeof(chunksize_T));
5609 }
5610 return;
5611 }
5612 ml_upd_lastbuf = buf;
5613 ml_upd_lastline = line;
5614 ml_upd_lastcurline = curline;
5615 ml_upd_lastcurix = curix;
5616}
5617
5618/*
5619 * Find offset for line or line with offset.
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005620 * Find line with offset if "lnum" is 0; return remaining offset in offp
5621 * Find offset of line if "lnum" > 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00005622 * return -1 if information is not available
5623 */
5624 long
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005625ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005626{
5627 linenr_T curline;
5628 int curix;
5629 long size;
5630 bhdr_T *hp;
5631 DATA_BL *dp;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005632 int count; // number of entries in block
Bram Moolenaar071d4272004-06-13 20:20:40 +00005633 int idx;
5634 int start_idx;
5635 int text_end;
5636 long offset;
5637 int len;
5638 int ffdos = (get_fileformat(buf) == EOL_DOS);
5639 int extra = 0;
5640
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005641 // take care of cached line first
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005642 ml_flush_line(curbuf);
5643
Bram Moolenaar071d4272004-06-13 20:20:40 +00005644 if (buf->b_ml.ml_usedchunks == -1
5645 || buf->b_ml.ml_chunksize == NULL
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005646 || lnum < 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005647 return -1;
5648
5649 if (offp == NULL)
5650 offset = 0;
5651 else
5652 offset = *offp;
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005653 if (lnum == 0 && offset <= 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005654 return 1; // Not a "find offset" and offset 0 _must_ be in line 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00005655 /*
5656 * Find the last chunk before the one containing our line. Last chunk is
5657 * special because it will never qualify
5658 */
5659 curline = 1;
5660 curix = size = 0;
5661 while (curix < buf->b_ml.ml_usedchunks - 1
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005662 && ((lnum != 0
5663 && lnum >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005664 || (offset != 0
5665 && offset > size + buf->b_ml.ml_chunksize[curix].mlcs_totalsize
5666 + ffdos * buf->b_ml.ml_chunksize[curix].mlcs_numlines)))
5667 {
5668 curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
5669 size += buf->b_ml.ml_chunksize[curix].mlcs_totalsize;
5670 if (offset && ffdos)
5671 size += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
5672 curix++;
5673 }
5674
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005675 while ((lnum != 0 && curline < lnum) || (offset != 0 && size < offset))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005676 {
5677 if (curline > buf->b_ml.ml_line_count
5678 || (hp = ml_find_line(buf, curline, ML_FIND)) == NULL)
5679 return -1;
5680 dp = (DATA_BL *)(hp->bh_data);
5681 count = (long)(buf->b_ml.ml_locked_high) -
5682 (long)(buf->b_ml.ml_locked_low) + 1;
5683 start_idx = idx = curline - buf->b_ml.ml_locked_low;
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005684 if (idx == 0) // first line in block, text at the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00005685 text_end = dp->db_txt_end;
5686 else
5687 text_end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005688 // Compute index of last line to use in this MEMLINE
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005689 if (lnum != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005690 {
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005691 if (curline + (count - idx) >= lnum)
5692 idx += lnum - curline - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005693 else
5694 idx = count - 1;
5695 }
5696 else
5697 {
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005698#ifdef FEAT_PROP_POPUP
Bram Moolenaar109ef122020-01-17 19:12:03 +01005699 size_t textprop_total = 0;
5700 size_t textprop_size = 0;
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005701 char_u *l1, *l2;
5702#endif
5703
Bram Moolenaar071d4272004-06-13 20:20:40 +00005704 extra = 0;
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005705 for (;;)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005706 {
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005707#ifdef FEAT_PROP_POPUP
5708 if (buf->b_has_textprop)
5709 {
5710 // compensate for the extra bytes taken by textprops
5711 l1 = (char_u *)dp + ((dp->db_index[idx]) & DB_INDEX_MASK);
5712 l2 = (char_u *)dp + (idx == 0 ? dp->db_txt_end
5713 : ((dp->db_index[idx - 1]) & DB_INDEX_MASK));
5714 textprop_size = (l2 - l1) - (STRLEN(l1) + 1);
5715 }
5716#endif
5717 if (!(offset >= size
5718 + text_end - (int)((dp->db_index[idx]) & DB_INDEX_MASK)
5719#ifdef FEAT_PROP_POPUP
Bram Moolenaar94b6fb72020-01-17 21:00:59 +01005720 - (long)(textprop_total + textprop_size)
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005721#endif
5722 + ffdos))
5723 break;
5724
Bram Moolenaar071d4272004-06-13 20:20:40 +00005725 if (ffdos)
5726 size++;
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005727#ifdef FEAT_PROP_POPUP
5728 textprop_total += textprop_size;
5729#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005730 if (idx == count - 1)
5731 {
5732 extra = 1;
5733 break;
5734 }
5735 idx++;
5736 }
5737 }
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01005738#ifdef FEAT_PROP_POPUP
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005739 if (buf->b_has_textprop)
5740 {
5741 int i;
5742
5743 // cannot use the db_index pointer, need to get the actual text
5744 // lengths.
5745 len = 0;
5746 for (i = start_idx; i <= idx; ++i)
Bram Moolenaar9df53b62020-01-13 20:40:51 +01005747 len += (int)STRLEN((char_u *)dp
5748 + ((dp->db_index[i]) & DB_INDEX_MASK)) + 1;
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005749 }
5750 else
5751#endif
5752 len = text_end - ((dp->db_index[idx]) & DB_INDEX_MASK);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005753 size += len;
5754 if (offset != 0 && size >= offset)
5755 {
5756 if (size + ffdos == offset)
5757 *offp = 0;
5758 else if (idx == start_idx)
5759 *offp = offset - size + len;
5760 else
5761 *offp = offset - size + len
5762 - (text_end - ((dp->db_index[idx - 1]) & DB_INDEX_MASK));
5763 curline += idx - start_idx + extra;
5764 if (curline > buf->b_ml.ml_line_count)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005765 return -1; // exactly one byte beyond the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00005766 return curline;
5767 }
5768 curline = buf->b_ml.ml_locked_high + 1;
5769 }
5770
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005771 if (lnum != 0)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005772 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005773 // Count extra CR characters.
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005774 if (ffdos)
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005775 size += lnum - 1;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005776
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005777 // Don't count the last line break if 'noeol' and ('bin' or
5778 // 'nofixeol').
Bram Moolenaar34d72d42015-07-17 14:18:08 +02005779 if ((!buf->b_p_fixeol || buf->b_p_bin) && !buf->b_p_eol
Bram Moolenaarc26f7c62018-08-20 22:53:04 +02005780 && lnum > buf->b_ml.ml_line_count)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005781 size -= ffdos + 1;
5782 }
5783
Bram Moolenaar071d4272004-06-13 20:20:40 +00005784 return size;
5785}
5786
5787/*
5788 * Goto byte in buffer with offset 'cnt'.
5789 */
5790 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005791goto_byte(long cnt)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005792{
5793 long boff = cnt;
5794 linenr_T lnum;
5795
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005796 ml_flush_line(curbuf); // cached line may be dirty
Bram Moolenaar071d4272004-06-13 20:20:40 +00005797 setpcmark();
5798 if (boff)
5799 --boff;
5800 lnum = ml_find_line_or_offset(curbuf, (linenr_T)0, &boff);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005801 if (lnum < 1) // past the end
Bram Moolenaar071d4272004-06-13 20:20:40 +00005802 {
5803 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
5804 curwin->w_curswant = MAXCOL;
5805 coladvance((colnr_T)MAXCOL);
5806 }
5807 else
5808 {
5809 curwin->w_cursor.lnum = lnum;
5810 curwin->w_cursor.col = (colnr_T)boff;
Bram Moolenaar943d2b52005-12-02 00:50:49 +00005811 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005812 curwin->w_set_curswant = TRUE;
5813 }
5814 check_cursor();
5815
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01005816 // Make sure the cursor is on the first byte of a multi-byte char.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005817 if (has_mbyte)
5818 mb_adjust_cursor();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005819}
5820#endif