blob: 812d10dc1a27f6a218879b4c6f36fc3a59a6abd9 [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
10/* for debugging */
Bram Moolenaarf9e3e092019-01-13 23:38:42 +010011/* #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 Moolenaar071d4272004-06-13 20:20:40 +000047#ifndef UNIX /* it's in os_unix.h for Unix */
48# include <time.h>
49#endif
50
Bram Moolenaar5a6404c2006-11-01 17:12:57 +000051#if defined(SASC) || defined(__amigaos4__)
Bram Moolenaar071d4272004-06-13 20:20:40 +000052# include <proto/dos.h> /* for Open() and Close() */
53#endif
54
55typedef 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 */
59
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +020060#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 */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +020066#define BLOCK0_ID1_C2 'd' /* block 0 id 1 'cm' 2 */
67
68#if defined(FEAT_CRYPT)
69static int id1_codes[] = {
70 BLOCK0_ID1_C0, /* CRYPT_M_ZIP */
71 BLOCK0_ID1_C1, /* CRYPT_M_BF */
72 BLOCK0_ID1_C2, /* CRYPT_M_BF2 */
73};
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{
81 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 */
85};
86
87/*
88 * A pointer block contains a list of branches in the tree.
89 */
90struct pointer_block
91{
92 short_u pb_id; /* ID for pointer block: PTR_ID */
Bram Moolenaar20a825a2010-05-31 21:27:30 +020093 short_u pb_count; /* number of pointers in this block */
Bram Moolenaar071d4272004-06-13 20:20:40 +000094 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 */
97};
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{
108 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)
114 * followed by empty space upto db_txt_start
115 * followed by the text in the lines until
116 * end of page */
117};
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
130#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 */
132
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000133#define B0_FNAME_SIZE_ORG 900 /* what it was in older versions */
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200134#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 Moolenaara8ffcbb2010-06-21 06:15:46 +0200162 char_u b0_id[2]; /* id for block 0: BLOCK0_ID0 and BLOCK0_ID1,
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200163 * BLOCK0_ID1_C0, BLOCK0_ID1_C1, etc. */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000164 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) */
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000171 char_u b0_fname[B0_FNAME_SIZE_ORG]; /* name of file being edited */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000172 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 */
176};
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
198/* 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. */
201#define B0_FF_MASK 3
202
203/* Swap file is in directory of edited file. Used to find the file from
204 * different mount points. */
205#define B0_SAME_DIR 4
206
207/* The 'fileencoding' is at the end of b0_fname[], with a NUL in front of it.
208 * When empty there is only the NUL. */
209#define B0_HAS_FENC 8
Bram Moolenaar071d4272004-06-13 20:20:40 +0000210
211#define STACK_INCR 5 /* nr of entries added to ml_stack at a time */
212
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 */
224#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 */
229
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200230/* argument for ml_upd_block0() */
231typedef enum {
232 UB_FNAME = 0 /* update timestamp and filename */
233 , UB_SAME_DIR /* update the B0_SAME_DIR flag */
234 , UB_CRYPT /* update crypt key */
235} 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);
246static int ml_append_int(buf_T *, linenr_T, char_u *, colnr_T, int, int);
247static int ml_delete_int(buf_T *, linenr_T, int);
248static char_u *findswapname(buf_T *, char_u **, char_u *);
249static void ml_flush_line(buf_T *);
250static bhdr_T *ml_new_data(memfile_T *, int, int);
251static bhdr_T *ml_new_ptr(memfile_T *);
252static bhdr_T *ml_find_line(buf_T *, linenr_T, int);
253static int ml_add_stack(buf_T *);
254static void ml_lineadd(buf_T *, int);
255static int b0_magic_wrong(ZERO_BL *);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000256#ifdef CHECK_INODE
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100257static int fnamecmp_ino(char_u *, char_u *, long);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000258#endif
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100259static void long_to_char(long, char_u *);
260static long char_to_long(char_u *);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200261#ifdef FEAT_CRYPT
Bram Moolenaar8767f522016-07-01 17:17:39 +0200262static cryptstate_T *ml_crypt_prepare(memfile_T *mfp, off_T offset, int reading);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200263#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000264#ifdef FEAT_BYTEOFF
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100265static void ml_updatechunk(buf_T *buf, long line, long len, int updtype);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000266#endif
267
268/*
Bram Moolenaar4770d092006-01-12 23:22:24 +0000269 * Open a new memline for "buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +0000270 *
Bram Moolenaar4770d092006-01-12 23:22:24 +0000271 * Return FAIL for failure, OK otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000272 */
273 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100274ml_open(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000275{
276 memfile_T *mfp;
277 bhdr_T *hp = NULL;
278 ZERO_BL *b0p;
279 PTR_BL *pp;
280 DATA_BL *dp;
281
Bram Moolenaar4770d092006-01-12 23:22:24 +0000282 /*
283 * init fields in memline struct
284 */
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200285 buf->b_ml.ml_stack_size = 0; /* no stack yet */
Bram Moolenaar4770d092006-01-12 23:22:24 +0000286 buf->b_ml.ml_stack = NULL; /* no stack yet */
287 buf->b_ml.ml_stack_top = 0; /* nothing in the stack */
288 buf->b_ml.ml_locked = NULL; /* no cached block */
289 buf->b_ml.ml_line_lnum = 0; /* no cached line */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000290#ifdef FEAT_BYTEOFF
Bram Moolenaar4770d092006-01-12 23:22:24 +0000291 buf->b_ml.ml_chunksize = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000292#endif
293
Bram Moolenaar5803ae62014-03-23 16:04:02 +0100294 if (cmdmod.noswapfile)
295 buf->b_p_swf = FALSE;
296
Bram Moolenaar4770d092006-01-12 23:22:24 +0000297 /*
298 * When 'updatecount' is non-zero swap file may be opened later.
299 */
300 if (p_uc && buf->b_p_swf)
301 buf->b_may_swap = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000302 else
Bram Moolenaar4770d092006-01-12 23:22:24 +0000303 buf->b_may_swap = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000304
Bram Moolenaar4770d092006-01-12 23:22:24 +0000305 /*
306 * Open the memfile. No swap file is created yet.
307 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000308 mfp = mf_open(NULL, 0);
309 if (mfp == NULL)
310 goto error;
311
Bram Moolenaar4770d092006-01-12 23:22:24 +0000312 buf->b_ml.ml_mfp = mfp;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200313#ifdef FEAT_CRYPT
314 mfp->mf_buffer = buf;
315#endif
Bram Moolenaar4770d092006-01-12 23:22:24 +0000316 buf->b_ml.ml_flags = ML_EMPTY;
317 buf->b_ml.ml_line_count = 1;
Bram Moolenaar592e0a22004-07-03 16:05:59 +0000318#ifdef FEAT_LINEBREAK
319 curwin->w_nrwidth_line_count = 0;
320#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000321
Bram Moolenaar071d4272004-06-13 20:20:40 +0000322/*
323 * fill block0 struct and write page 0
324 */
325 if ((hp = mf_new(mfp, FALSE, 1)) == NULL)
326 goto error;
327 if (hp->bh_bnum != 0)
328 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100329 iemsg(_("E298: Didn't get block nr 0?"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000330 goto error;
331 }
332 b0p = (ZERO_BL *)(hp->bh_data);
333
334 b0p->b0_id[0] = BLOCK0_ID0;
335 b0p->b0_id[1] = BLOCK0_ID1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000336 b0p->b0_magic_long = (long)B0_MAGIC_LONG;
337 b0p->b0_magic_int = (int)B0_MAGIC_INT;
338 b0p->b0_magic_short = (short)B0_MAGIC_SHORT;
339 b0p->b0_magic_char = B0_MAGIC_CHAR;
Bram Moolenaar22c10562018-05-26 17:35:27 +0200340 mch_memmove(b0p->b0_version, "VIM ", 4);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000341 STRNCPY(b0p->b0_version + 4, Version, 6);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000342 long_to_char((long)mfp->mf_page_size, b0p->b0_page_size);
Bram Moolenaar4770d092006-01-12 23:22:24 +0000343
Bram Moolenaar76b92b22006-03-24 22:46:53 +0000344#ifdef FEAT_SPELL
345 if (!buf->b_spell)
346#endif
Bram Moolenaar4770d092006-01-12 23:22:24 +0000347 {
348 b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0;
349 b0p->b0_flags = get_fileformat(buf) + 1;
350 set_b0_fname(b0p, buf);
351 (void)get_user_name(b0p->b0_uname, B0_UNAME_SIZE);
352 b0p->b0_uname[B0_UNAME_SIZE - 1] = NUL;
353 mch_get_host_name(b0p->b0_hname, B0_HNAME_SIZE);
354 b0p->b0_hname[B0_HNAME_SIZE - 1] = NUL;
355 long_to_char(mch_get_pid(), b0p->b0_pid);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200356#ifdef FEAT_CRYPT
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200357 ml_set_b0_crypt(buf, b0p);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200358#endif
Bram Moolenaar4770d092006-01-12 23:22:24 +0000359 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000360
361 /*
362 * Always sync block number 0 to disk, so we can check the file name in
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200363 * the swap file in findswapname(). Don't do this for a help files or
364 * a spell buffer though.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000365 * Only works when there's a swapfile, otherwise it's done when the file
366 * is created.
367 */
368 mf_put(mfp, hp, TRUE, FALSE);
Bram Moolenaar4770d092006-01-12 23:22:24 +0000369 if (!buf->b_help && !B_SPELL(buf))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000370 (void)mf_sync(mfp, 0);
371
Bram Moolenaar4770d092006-01-12 23:22:24 +0000372 /*
373 * Fill in root pointer block and write page 1.
374 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000375 if ((hp = ml_new_ptr(mfp)) == NULL)
376 goto error;
377 if (hp->bh_bnum != 1)
378 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100379 iemsg(_("E298: Didn't get block nr 1?"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000380 goto error;
381 }
382 pp = (PTR_BL *)(hp->bh_data);
383 pp->pb_count = 1;
384 pp->pb_pointer[0].pe_bnum = 2;
385 pp->pb_pointer[0].pe_page_count = 1;
386 pp->pb_pointer[0].pe_old_lnum = 1;
387 pp->pb_pointer[0].pe_line_count = 1; /* line count after insertion */
388 mf_put(mfp, hp, TRUE, FALSE);
389
Bram Moolenaar4770d092006-01-12 23:22:24 +0000390 /*
391 * Allocate first data block and create an empty line 1.
392 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000393 if ((hp = ml_new_data(mfp, FALSE, 1)) == NULL)
394 goto error;
395 if (hp->bh_bnum != 2)
396 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100397 iemsg(_("E298: Didn't get block nr 2?"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000398 goto error;
399 }
400
401 dp = (DATA_BL *)(hp->bh_data);
402 dp->db_index[0] = --dp->db_txt_start; /* at end of block */
403 dp->db_free -= 1 + INDEX_SIZE;
404 dp->db_line_count = 1;
Bram Moolenaarf05da212009-11-17 16:13:15 +0000405 *((char_u *)dp + dp->db_txt_start) = NUL; /* empty line */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000406
407 return OK;
408
409error:
410 if (mfp != NULL)
411 {
412 if (hp)
413 mf_put(mfp, hp, FALSE, FALSE);
414 mf_close(mfp, TRUE); /* will also free(mfp->mf_fname) */
415 }
Bram Moolenaar4770d092006-01-12 23:22:24 +0000416 buf->b_ml.ml_mfp = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000417 return FAIL;
418}
419
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200420#if defined(FEAT_CRYPT) || defined(PROTO)
421/*
Bram Moolenaar2be79502014-08-13 21:58:28 +0200422 * Prepare encryption for "buf" for the current key and method.
423 */
424 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100425ml_set_mfp_crypt(buf_T *buf)
Bram Moolenaar2be79502014-08-13 21:58:28 +0200426{
427 if (*buf->b_p_key != NUL)
428 {
429 int method_nr = crypt_get_method_nr(buf);
430
431 if (method_nr > CRYPT_M_ZIP)
432 {
433 /* Generate a seed and store it in the memfile. */
434 sha2_seed(buf->b_ml.ml_mfp->mf_seed, MF_SEED_LEN, NULL, 0);
435 }
436 }
437}
438
439/*
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200440 * Prepare encryption for "buf" with block 0 "b0p".
441 */
442 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100443ml_set_b0_crypt(buf_T *buf, ZERO_BL *b0p)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200444{
445 if (*buf->b_p_key == NUL)
446 b0p->b0_id[1] = BLOCK0_ID1;
447 else
448 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200449 int method_nr = crypt_get_method_nr(buf);
450
451 b0p->b0_id[1] = id1_codes[method_nr];
452 if (method_nr > CRYPT_M_ZIP)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200453 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200454 /* Generate a seed and store it in block 0 and in the memfile. */
455 sha2_seed(&b0p->b0_seed, MF_SEED_LEN, NULL, 0);
456 mch_memmove(buf->b_ml.ml_mfp->mf_seed, &b0p->b0_seed, MF_SEED_LEN);
457 }
458 }
459}
460
461/*
462 * Called after the crypt key or 'cryptmethod' was changed for "buf".
463 * Will apply this to the swapfile.
464 * "old_key" is the previous key. It is equal to buf->b_p_key when
465 * 'cryptmethod' is changed.
Bram Moolenaar49771f42010-07-20 17:32:38 +0200466 * "old_cm" is the previous 'cryptmethod'. It is equal to the current
467 * 'cryptmethod' when 'key' is changed.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200468 */
469 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100470ml_set_crypt_key(
471 buf_T *buf,
472 char_u *old_key,
473 char_u *old_cm)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200474{
475 memfile_T *mfp = buf->b_ml.ml_mfp;
476 bhdr_T *hp;
477 int page_count;
478 int idx;
479 long error;
480 infoptr_T *ip;
481 PTR_BL *pp;
482 DATA_BL *dp;
483 blocknr_T bnum;
484 int top;
Bram Moolenaarbc563362015-06-09 18:35:25 +0200485 int old_method;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200486
Bram Moolenaar3832c462010-08-04 15:32:46 +0200487 if (mfp == NULL)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200488 return; /* no memfile yet, nothing to do */
Bram Moolenaarbc563362015-06-09 18:35:25 +0200489 old_method = crypt_method_nr_from_name(old_cm);
490
491 /* First make sure the swapfile is in a consistent state, using the old
492 * key and method. */
493 {
494 char_u *new_key = buf->b_p_key;
495 char_u *new_buf_cm = buf->b_p_cm;
496
497 buf->b_p_key = old_key;
498 buf->b_p_cm = old_cm;
499 ml_preserve(buf, FALSE);
500 buf->b_p_key = new_key;
501 buf->b_p_cm = new_buf_cm;
502 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200503
504 /* Set the key, method and seed to be used for reading, these must be the
505 * old values. */
506 mfp->mf_old_key = old_key;
Bram Moolenaarbc563362015-06-09 18:35:25 +0200507 mfp->mf_old_cm = old_method;
508 if (old_method > 0 && *old_key != NUL)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200509 mch_memmove(mfp->mf_old_seed, mfp->mf_seed, MF_SEED_LEN);
510
511 /* Update block 0 with the crypt flag and may set a new seed. */
512 ml_upd_block0(buf, UB_CRYPT);
513
514 if (mfp->mf_infile_count > 2)
515 {
516 /*
517 * Need to read back all data blocks from disk, decrypt them with the
518 * old key/method and mark them to be written. The algorithm is
519 * similar to what happens in ml_recover(), but we skip negative block
520 * numbers.
521 */
522 ml_flush_line(buf); /* flush buffered line */
523 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); /* flush locked block */
524
525 hp = NULL;
526 bnum = 1; /* start with block 1 */
527 page_count = 1; /* which is 1 page */
528 idx = 0; /* start with first index in block 1 */
529 error = 0;
530 buf->b_ml.ml_stack_top = 0;
Bram Moolenaard23a8232018-02-10 18:45:26 +0100531 VIM_CLEAR(buf->b_ml.ml_stack);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200532 buf->b_ml.ml_stack_size = 0; /* no stack yet */
533
534 for ( ; !got_int; line_breakcheck())
535 {
536 if (hp != NULL)
537 mf_put(mfp, hp, FALSE, FALSE); /* release previous block */
538
539 /* get the block (pointer or data) */
540 if ((hp = mf_get(mfp, (blocknr_T)bnum, page_count)) == NULL)
541 {
542 if (bnum == 1)
543 break;
544 ++error;
545 }
546 else
547 {
548 pp = (PTR_BL *)(hp->bh_data);
549 if (pp->pb_id == PTR_ID) /* it is a pointer block */
550 {
551 if (pp->pb_count == 0)
552 {
553 /* empty block? */
554 ++error;
555 }
556 else if (idx < (int)pp->pb_count) /* go a block deeper */
557 {
558 if (pp->pb_pointer[idx].pe_bnum < 0)
559 {
Bram Moolenaarbc563362015-06-09 18:35:25 +0200560 /* Skip data block with negative block number.
561 * Should not happen, because of the ml_preserve()
562 * above. Get same block again for next index. */
Bram Moolenaar4b7214e2019-01-03 21:55:32 +0100563 ++idx;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200564 continue;
565 }
566
567 /* going one block deeper in the tree, new entry in
568 * stack */
569 if ((top = ml_add_stack(buf)) < 0)
570 {
571 ++error;
572 break; /* out of memory */
573 }
574 ip = &(buf->b_ml.ml_stack[top]);
575 ip->ip_bnum = bnum;
576 ip->ip_index = idx;
577
578 bnum = pp->pb_pointer[idx].pe_bnum;
579 page_count = pp->pb_pointer[idx].pe_page_count;
Bram Moolenaarbc563362015-06-09 18:35:25 +0200580 idx = 0;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200581 continue;
582 }
583 }
584 else /* not a pointer block */
585 {
586 dp = (DATA_BL *)(hp->bh_data);
587 if (dp->db_id != DATA_ID) /* block id wrong */
588 ++error;
589 else
590 {
591 /* It is a data block, need to write it back to disk. */
592 mf_put(mfp, hp, TRUE, FALSE);
593 hp = NULL;
594 }
595 }
596 }
597
598 if (buf->b_ml.ml_stack_top == 0) /* finished */
599 break;
600
601 /* go one block up in the tree */
602 ip = &(buf->b_ml.ml_stack[--(buf->b_ml.ml_stack_top)]);
603 bnum = ip->ip_bnum;
604 idx = ip->ip_index + 1; /* go to next index */
605 page_count = 1;
606 }
Bram Moolenaarbc563362015-06-09 18:35:25 +0200607 if (hp != NULL)
608 mf_put(mfp, hp, FALSE, FALSE); /* release previous block */
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +0100609
610 if (error > 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100611 emsg(_("E843: Error while updating swap file crypt"));
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200612 }
613
614 mfp->mf_old_key = NULL;
615}
616#endif
617
Bram Moolenaar071d4272004-06-13 20:20:40 +0000618/*
619 * ml_setname() is called when the file name of "buf" has been changed.
620 * It may rename the swap file.
621 */
622 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100623ml_setname(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000624{
625 int success = FALSE;
626 memfile_T *mfp;
627 char_u *fname;
628 char_u *dirp;
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100629#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000630 char_u *p;
631#endif
632
633 mfp = buf->b_ml.ml_mfp;
634 if (mfp->mf_fd < 0) /* there is no swap file yet */
635 {
636 /*
637 * When 'updatecount' is 0 and 'noswapfile' there is no swap file.
638 * For help files we will make a swap file now.
639 */
Bram Moolenaar5803ae62014-03-23 16:04:02 +0100640 if (p_uc != 0 && !cmdmod.noswapfile)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000641 ml_open_file(buf); /* create a swap file */
642 return;
643 }
644
645 /*
646 * Try all directories in the 'directory' option.
647 */
648 dirp = p_dir;
649 for (;;)
650 {
651 if (*dirp == NUL) /* tried all directories, fail */
652 break;
Bram Moolenaar8fc061c2004-12-29 21:03:02 +0000653 fname = findswapname(buf, &dirp, mfp->mf_fname);
654 /* alloc's fname */
Bram Moolenaarf541c362011-10-26 11:44:18 +0200655 if (dirp == NULL) /* out of memory */
656 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000657 if (fname == NULL) /* no file name found for this dir */
658 continue;
659
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100660#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000661 /*
662 * Set full pathname for swap file now, because a ":!cd dir" may
663 * change directory without us knowing it.
664 */
665 p = FullName_save(fname, FALSE);
666 vim_free(fname);
667 fname = p;
668 if (fname == NULL)
669 continue;
670#endif
671 /* if the file name is the same we don't have to do anything */
672 if (fnamecmp(fname, mfp->mf_fname) == 0)
673 {
674 vim_free(fname);
675 success = TRUE;
676 break;
677 }
678 /* need to close the swap file before renaming */
679 if (mfp->mf_fd >= 0)
680 {
681 close(mfp->mf_fd);
682 mfp->mf_fd = -1;
683 }
684
685 /* try to rename the swap file */
686 if (vim_rename(mfp->mf_fname, fname) == 0)
687 {
688 success = TRUE;
689 vim_free(mfp->mf_fname);
690 mfp->mf_fname = fname;
691 vim_free(mfp->mf_ffname);
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100692#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000693 mfp->mf_ffname = NULL; /* mf_fname is full pathname already */
694#else
695 mf_set_ffname(mfp);
696#endif
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200697 ml_upd_block0(buf, UB_SAME_DIR);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000698 break;
699 }
700 vim_free(fname); /* this fname didn't work, try another */
701 }
702
703 if (mfp->mf_fd == -1) /* need to (re)open the swap file */
704 {
705 mfp->mf_fd = mch_open((char *)mfp->mf_fname, O_RDWR | O_EXTRA, 0);
706 if (mfp->mf_fd < 0)
707 {
708 /* could not (re)open the swap file, what can we do???? */
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100709 emsg(_("E301: Oops, lost the swap file!!!"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000710 return;
711 }
Bram Moolenaarf05da212009-11-17 16:13:15 +0000712#ifdef HAVE_FD_CLOEXEC
713 {
714 int fdflags = fcntl(mfp->mf_fd, F_GETFD);
715 if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0)
Bram Moolenaarfbc4b4d2016-02-07 15:14:01 +0100716 (void)fcntl(mfp->mf_fd, F_SETFD, fdflags | FD_CLOEXEC);
Bram Moolenaarf05da212009-11-17 16:13:15 +0000717 }
718#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000719 }
720 if (!success)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100721 emsg(_("E302: Could not rename swap file"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000722}
723
724/*
725 * Open a file for the memfile for all buffers that are not readonly or have
726 * been modified.
727 * Used when 'updatecount' changes from zero to non-zero.
728 */
729 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100730ml_open_files(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000731{
732 buf_T *buf;
733
Bram Moolenaar29323592016-07-24 22:04:11 +0200734 FOR_ALL_BUFFERS(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000735 if (!buf->b_p_ro || buf->b_changed)
736 ml_open_file(buf);
737}
738
739/*
740 * Open a swap file for an existing memfile, if there is no swap file yet.
741 * If we are unable to find a file name, mf_fname will be NULL
742 * and the memfile will be in memory only (no recovery possible).
743 */
744 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100745ml_open_file(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000746{
747 memfile_T *mfp;
748 char_u *fname;
749 char_u *dirp;
750
751 mfp = buf->b_ml.ml_mfp;
Bram Moolenaar5803ae62014-03-23 16:04:02 +0100752 if (mfp == NULL || mfp->mf_fd >= 0 || !buf->b_p_swf || cmdmod.noswapfile)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000753 return; /* nothing to do */
754
Bram Moolenaara1956f62006-03-12 22:18:00 +0000755#ifdef FEAT_SPELL
Bram Moolenaar4770d092006-01-12 23:22:24 +0000756 /* For a spell buffer use a temp file name. */
757 if (buf->b_spell)
758 {
Bram Moolenaare5c421c2015-03-31 13:33:08 +0200759 fname = vim_tempname('s', FALSE);
Bram Moolenaar4770d092006-01-12 23:22:24 +0000760 if (fname != NULL)
761 (void)mf_open_file(mfp, fname); /* consumes fname! */
762 buf->b_may_swap = FALSE;
763 return;
764 }
765#endif
766
Bram Moolenaar071d4272004-06-13 20:20:40 +0000767 /*
768 * Try all directories in 'directory' option.
769 */
770 dirp = p_dir;
771 for (;;)
772 {
773 if (*dirp == NUL)
774 break;
Bram Moolenaare242b832010-06-24 05:39:03 +0200775 /* There is a small chance that between choosing the swap file name
776 * and creating it, another Vim creates the file. In that case the
Bram Moolenaar071d4272004-06-13 20:20:40 +0000777 * creation will fail and we will use another directory. */
Bram Moolenaar8fc061c2004-12-29 21:03:02 +0000778 fname = findswapname(buf, &dirp, NULL); /* allocates fname */
Bram Moolenaarf541c362011-10-26 11:44:18 +0200779 if (dirp == NULL)
780 break; /* out of memory */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000781 if (fname == NULL)
782 continue;
783 if (mf_open_file(mfp, fname) == OK) /* consumes fname! */
784 {
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100785#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000786 /*
787 * set full pathname for swap file now, because a ":!cd dir" may
788 * change directory without us knowing it.
789 */
790 mf_fullname(mfp);
791#endif
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200792 ml_upd_block0(buf, UB_SAME_DIR);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000793
Bram Moolenaar071d4272004-06-13 20:20:40 +0000794 /* Flush block zero, so others can read it */
795 if (mf_sync(mfp, MFS_ZERO) == OK)
Bram Moolenaarc32840f2006-01-14 21:23:38 +0000796 {
797 /* Mark all blocks that should be in the swapfile as dirty.
798 * Needed for when the 'swapfile' option was reset, so that
799 * the swap file was deleted, and then on again. */
800 mf_set_dirty(mfp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000801 break;
Bram Moolenaarc32840f2006-01-14 21:23:38 +0000802 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000803 /* Writing block 0 failed: close the file and try another dir */
804 mf_close_file(buf, FALSE);
805 }
806 }
807
808 if (mfp->mf_fname == NULL) /* Failed! */
809 {
810 need_wait_return = TRUE; /* call wait_return later */
811 ++no_wait_return;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100812 (void)semsg(_("E303: Unable to open swap file for \"%s\", recovery impossible"),
Bram Moolenaare1704ba2012-10-03 18:25:00 +0200813 buf_spname(buf) != NULL ? buf_spname(buf) : buf->b_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000814 --no_wait_return;
815 }
816
817 /* don't try to open a swap file again */
818 buf->b_may_swap = FALSE;
819}
820
821/*
822 * If still need to create a swap file, and starting to edit a not-readonly
823 * file, or reading into an existing buffer, create a swap file now.
824 */
825 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100826check_need_swap(
Bram Moolenaar2f0f8712018-08-21 18:50:18 +0200827 int newfile) // reading file into new buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +0000828{
Bram Moolenaar2f0f8712018-08-21 18:50:18 +0200829 int old_msg_silent = msg_silent; // might be reset by an E325 message
830
Bram Moolenaar071d4272004-06-13 20:20:40 +0000831 if (curbuf->b_may_swap && (!curbuf->b_p_ro || !newfile))
832 ml_open_file(curbuf);
Bram Moolenaar2f0f8712018-08-21 18:50:18 +0200833 msg_silent = old_msg_silent;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000834}
835
836/*
837 * Close memline for buffer 'buf'.
838 * If 'del_file' is TRUE, delete the swap file
839 */
840 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100841ml_close(buf_T *buf, int del_file)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000842{
843 if (buf->b_ml.ml_mfp == NULL) /* not open */
844 return;
845 mf_close(buf->b_ml.ml_mfp, del_file); /* close the .swp file */
846 if (buf->b_ml.ml_line_lnum != 0 && (buf->b_ml.ml_flags & ML_LINE_DIRTY))
847 vim_free(buf->b_ml.ml_line_ptr);
848 vim_free(buf->b_ml.ml_stack);
849#ifdef FEAT_BYTEOFF
Bram Moolenaard23a8232018-02-10 18:45:26 +0100850 VIM_CLEAR(buf->b_ml.ml_chunksize);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000851#endif
852 buf->b_ml.ml_mfp = NULL;
853
854 /* Reset the "recovered" flag, give the ATTENTION prompt the next time
855 * this buffer is loaded. */
856 buf->b_flags &= ~BF_RECOVERED;
857}
858
859/*
860 * Close all existing memlines and memfiles.
861 * Only used when exiting.
862 * When 'del_file' is TRUE, delete the memfiles.
Bram Moolenaar81bf7082005-02-12 14:31:42 +0000863 * But don't delete files that were ":preserve"d when we are POSIX compatible.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000864 */
865 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100866ml_close_all(int del_file)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000867{
868 buf_T *buf;
869
Bram Moolenaar29323592016-07-24 22:04:11 +0200870 FOR_ALL_BUFFERS(buf)
Bram Moolenaar81bf7082005-02-12 14:31:42 +0000871 ml_close(buf, del_file && ((buf->b_flags & BF_PRESERVED) == 0
872 || vim_strchr(p_cpo, CPO_PRESERVE) == NULL));
Bram Moolenaar34b466e2013-11-28 17:41:46 +0100873#ifdef FEAT_SPELL
874 spell_delete_wordlist(); /* delete the internal wordlist */
875#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000876#ifdef TEMPDIRNAMES
Bram Moolenaar34b466e2013-11-28 17:41:46 +0100877 vim_deltempdir(); /* delete created temp directory */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000878#endif
879}
880
881/*
882 * Close all memfiles for not modified buffers.
883 * Only use just before exiting!
884 */
885 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100886ml_close_notmod(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000887{
888 buf_T *buf;
889
Bram Moolenaar29323592016-07-24 22:04:11 +0200890 FOR_ALL_BUFFERS(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000891 if (!bufIsChanged(buf))
892 ml_close(buf, TRUE); /* close all not-modified buffers */
893}
894
895/*
896 * Update the timestamp in the .swp file.
897 * Used when the file has been written.
898 */
899 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100900ml_timestamp(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000901{
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200902 ml_upd_block0(buf, UB_FNAME);
903}
904
905/*
906 * Return FAIL when the ID of "b0p" is wrong.
907 */
908 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100909ml_check_b0_id(ZERO_BL *b0p)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200910{
911 if (b0p->b0_id[0] != BLOCK0_ID0
912 || (b0p->b0_id[1] != BLOCK0_ID1
913 && b0p->b0_id[1] != BLOCK0_ID1_C0
Bram Moolenaar8f4ac012014-08-10 13:38:34 +0200914 && b0p->b0_id[1] != BLOCK0_ID1_C1
915 && b0p->b0_id[1] != BLOCK0_ID1_C2)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200916 )
917 return FAIL;
918 return OK;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000919}
920
921/*
922 * Update the timestamp or the B0_SAME_DIR flag of the .swp file.
923 */
924 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100925ml_upd_block0(buf_T *buf, upd_block0_T what)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000926{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000927 memfile_T *mfp;
928 bhdr_T *hp;
929 ZERO_BL *b0p;
930
931 mfp = buf->b_ml.ml_mfp;
Bram Moolenaar2be79502014-08-13 21:58:28 +0200932 if (mfp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000933 return;
Bram Moolenaar2be79502014-08-13 21:58:28 +0200934 hp = mf_get(mfp, (blocknr_T)0, 1);
935 if (hp == NULL)
936 {
937#ifdef FEAT_CRYPT
938 /* Possibly update the seed in the memfile before there is a block0. */
939 if (what == UB_CRYPT)
940 ml_set_mfp_crypt(buf);
941#endif
942 return;
943 }
944
Bram Moolenaar071d4272004-06-13 20:20:40 +0000945 b0p = (ZERO_BL *)(hp->bh_data);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200946 if (ml_check_b0_id(b0p) == FAIL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100947 iemsg(_("E304: ml_upd_block0(): Didn't get block 0??"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000948 else
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000949 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200950 if (what == UB_FNAME)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000951 set_b0_fname(b0p, buf);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200952#ifdef FEAT_CRYPT
953 else if (what == UB_CRYPT)
954 ml_set_b0_crypt(buf, b0p);
955#endif
956 else /* what == UB_SAME_DIR */
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000957 set_b0_dir_flag(b0p, buf);
958 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000959 mf_put(mfp, hp, TRUE, FALSE);
960}
961
962/*
963 * Write file name and timestamp into block 0 of a swap file.
964 * Also set buf->b_mtime.
965 * Don't use NameBuff[]!!!
966 */
967 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100968set_b0_fname(ZERO_BL *b0p, buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000969{
Bram Moolenaar8767f522016-07-01 17:17:39 +0200970 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000971
972 if (buf->b_ffname == NULL)
973 b0p->b0_fname[0] = NUL;
974 else
975 {
Bram Moolenaar48e330a2016-02-23 14:53:34 +0100976#if defined(MSWIN) || defined(AMIGA)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000977 /* Systems that cannot translate "~user" back into a path: copy the
978 * file name unmodified. Do use slashes instead of backslashes for
979 * portability. */
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200980 vim_strncpy(b0p->b0_fname, buf->b_ffname, B0_FNAME_SIZE_CRYPT - 1);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000981# ifdef BACKSLASH_IN_FILENAME
982 forward_slash(b0p->b0_fname);
983# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000984#else
985 size_t flen, ulen;
986 char_u uname[B0_UNAME_SIZE];
987
988 /*
989 * For a file under the home directory of the current user, we try to
990 * replace the home directory path with "~user". This helps when
991 * editing the same file on different machines over a network.
992 * First replace home dir path with "~/" with home_replace().
993 * Then insert the user name to get "~user/".
994 */
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +0200995 home_replace(NULL, buf->b_ffname, b0p->b0_fname,
996 B0_FNAME_SIZE_CRYPT, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000997 if (b0p->b0_fname[0] == '~')
998 {
999 flen = STRLEN(b0p->b0_fname);
1000 /* If there is no user name or it is too long, don't use "~/" */
1001 if (get_user_name(uname, B0_UNAME_SIZE) == FAIL
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001002 || (ulen = STRLEN(uname)) + flen > B0_FNAME_SIZE_CRYPT - 1)
1003 vim_strncpy(b0p->b0_fname, buf->b_ffname,
1004 B0_FNAME_SIZE_CRYPT - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001005 else
1006 {
1007 mch_memmove(b0p->b0_fname + ulen + 1, b0p->b0_fname + 1, flen);
1008 mch_memmove(b0p->b0_fname + 1, uname, ulen);
1009 }
1010 }
1011#endif
1012 if (mch_stat((char *)buf->b_ffname, &st) >= 0)
1013 {
1014 long_to_char((long)st.st_mtime, b0p->b0_mtime);
1015#ifdef CHECK_INODE
1016 long_to_char((long)st.st_ino, b0p->b0_ino);
1017#endif
1018 buf_store_time(buf, &st, buf->b_ffname);
1019 buf->b_mtime_read = buf->b_mtime;
1020 }
1021 else
1022 {
1023 long_to_char(0L, b0p->b0_mtime);
1024#ifdef CHECK_INODE
1025 long_to_char(0L, b0p->b0_ino);
1026#endif
1027 buf->b_mtime = 0;
1028 buf->b_mtime_read = 0;
1029 buf->b_orig_size = 0;
1030 buf->b_orig_mode = 0;
1031 }
1032 }
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001033
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001034 /* Also add the 'fileencoding' if there is room. */
1035 add_b0_fenc(b0p, curbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001036}
1037
1038/*
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001039 * Update the B0_SAME_DIR flag of the swap file. It's set if the file and the
1040 * swapfile for "buf" are in the same directory.
1041 * This is fail safe: if we are not sure the directories are equal the flag is
1042 * not set.
1043 */
1044 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001045set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001046{
1047 if (same_directory(buf->b_ml.ml_mfp->mf_fname, buf->b_ffname))
1048 b0p->b0_flags |= B0_SAME_DIR;
1049 else
1050 b0p->b0_flags &= ~B0_SAME_DIR;
1051}
1052
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001053/*
1054 * When there is room, add the 'fileencoding' to block zero.
1055 */
1056 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001057add_b0_fenc(
1058 ZERO_BL *b0p,
1059 buf_T *buf)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001060{
1061 int n;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001062 int size = B0_FNAME_SIZE_NOCRYPT;
1063
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01001064#ifdef FEAT_CRYPT
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001065 /* Without encryption use the same offset as in Vim 7.2 to be compatible.
1066 * With encryption it's OK to move elsewhere, the swap file is not
1067 * compatible anyway. */
1068 if (*buf->b_p_key != NUL)
1069 size = B0_FNAME_SIZE_CRYPT;
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01001070#endif
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001071
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00001072 n = (int)STRLEN(buf->b_p_fenc);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001073 if ((int)STRLEN(b0p->b0_fname) + n + 1 > size)
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001074 b0p->b0_flags &= ~B0_HAS_FENC;
1075 else
1076 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001077 mch_memmove((char *)b0p->b0_fname + size - n,
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001078 (char *)buf->b_p_fenc, (size_t)n);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001079 *(b0p->b0_fname + size - n - 1) = NUL;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001080 b0p->b0_flags |= B0_HAS_FENC;
1081 }
1082}
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001083
1084
1085/*
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001086 * Try to recover curbuf from the .swp file.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001087 */
1088 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001089ml_recover(void)
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;
1136 if (fname == NULL) /* When there is no file name */
1137 fname = (char_u *)"";
1138 len = (int)STRLEN(fname);
1139 if (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 Moolenaara8ffcbb2010-06-21 06:15:46 +02001151 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
1157 /* count the number of matching swap files */
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001158 len = recover_names(fname, FALSE, 0, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001159 if (len == 0) /* no swap files found */
1160 {
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 }
1164 if (len == 1) /* one swap file found, use it */
1165 i = 1;
1166 else /* several swap files found, choose */
1167 {
1168 /* 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 }
1176 /* 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 Moolenaar071d4272004-06-13 20:20:40 +00001180 goto theend; /* out of memory */
1181
1182 /* 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 Moolenaar071d4272004-06-13 20:20:40 +00001190 buf = (buf_T *)alloc((unsigned)sizeof(buf_T));
1191 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 Moolenaar071d4272004-06-13 20:20:40 +00001197 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 */
1202 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 */
1211 p = vim_strsave(fname_used); /* save "fname_used" for the message:
1212 mf_open() will consume "fname_used"! */
1213 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 Moolenaare242b832010-06-24 05:39:03 +02001276 /* 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 Moolenaar071d4272004-06-13 20:20:40 +00001318 mfp->mf_blocknr_max = 0; /* no file or empty file */
1319 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
1323 /* need to reallocate the memory used to store the data */
1324 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
1365 /* Get the 'fileformat' and 'fileencoding' from block zero. */
1366 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
1372 /* Use the same size as in add_b0_fenc(). */
1373 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 Moolenaarf506c5b2010-06-22 06:28:58 +02001378 b0_fenc = vim_strnsave(p, (int)(b0p->b0_fname + fnsize - p));
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001379 }
1380
Bram Moolenaar071d4272004-06-13 20:20:40 +00001381 mf_put(mfp, hp, FALSE, FALSE); /* release block 0 */
1382 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))
1389 ml_delete((linenr_T)1, FALSE);
1390
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 {
1403 /* Need to ask the user for the crypt key. If this fails we continue
1404 * without a key, will probably get garbage text. */
1405 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 Moolenaar1cd871b2004-12-19 22:46:22 +00001428 /* Use the 'fileformat' and 'fileencoding' as stored in the swap file. */
1429 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 }
1436 unchanged(curbuf, TRUE);
1437
Bram Moolenaar071d4272004-06-13 20:20:40 +00001438 bnum = 1; /* start with block 1 */
1439 page_count = 1; /* which is 1 page */
1440 lnum = 0; /* append after line 0 in curbuf */
1441 line_count = 0;
1442 idx = 0; /* start with first index in block 1 */
1443 error = 0;
1444 buf->b_ml.ml_stack_top = 0;
1445 buf->b_ml.ml_stack = NULL;
1446 buf->b_ml.ml_stack_size = 0; /* no stack yet */
1447
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)
1457 mf_put(mfp, hp, FALSE, FALSE); /* release previous block */
1458
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 }
1473 else /* there is a block */
1474 {
1475 pp = (PTR_BL *)(hp->bh_data);
1476 if (pp->pb_id == PTR_ID) /* it is a pointer block */
1477 {
1478 /* check line count when using pointer block first time */
1479 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 }
1497 else if (idx < (int)pp->pb_count) /* go a block deeper */
1498 {
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 }
1522 ++idx; /* get same block again for next index */
1523 continue;
1524 }
1525
1526 /*
1527 * going one block deeper in the tree
1528 */
1529 if ((top = ml_add_stack(buf)) < 0) /* new entry in stack */
1530 {
1531 ++error;
1532 break; /* out of memory */
1533 }
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 }
1545 else /* not a pointer block */
1546 {
1547 dp = (DATA_BL *)(hp->bh_data);
1548 if (dp->db_id != DATA_ID) /* block id wrong */
1549 {
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
1580 /* make sure there is a NUL at the end of the block */
1581 *((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
1615 if (buf->b_ml.ml_stack_top == 0) /* finished */
1616 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;
1623 idx = ip->ip_index + 1; /* go to next index */
1624 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 {
1636 /* Recovering an empty file results in two lines and the first line is
1637 * empty. Don't set the modified flag then. */
1638 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 {
1648 /* Need to copy one line, fetching the other one may flush it. */
1649 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))
1667 ml_delete(curbuf->b_ml.ml_line_count, FALSE);
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);
1711 mf_close(mfp, FALSE); /* will also vim_free(mfp->mf_fname) */
1712 }
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(
1745 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 Moolenaara8ffcbb2010-06-21 06:15:46 +02001768 /* Expand symlink in the file name, because the swap file is created
1769 * with the actual file instead of with the symlink. */
1770 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 Moolenaar9dbe4752010-05-14 17:52:42 +02001779 /* 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 */
1788 dir_name = alloc((unsigned)STRLEN(p_dir) + 1);
1789 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
1799 if (dir_name[0] == '.' && dir_name[1] == NUL) /* check current dir */
1800 {
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 Moolenaar2cc93182006-10-10 19:56:03 +00001809 /* 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 }
1826 else /* check directory dir_name */
1827 {
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 Moolenaar2cc93182006-10-10 19:56:03 +00001836 /* 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 {
1858 /* 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
1877 /* check for out-of-memory */
1878 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,
1890 EW_KEEPALL|EW_FILE|EW_SILENT) == FAIL)
1891 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 {
1912 if (mch_stat((char *)swapname, &st) != -1) /* It exists! */
1913 {
1914 files = (char_u **)alloc((unsigned)sizeof(char_u *));
1915 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)
1933 if (fullpathcmp(p, files[i], TRUE) & FPC_SAME)
1934 {
Bram Moolenaar9439cdd2009-04-22 13:39:36 +00001935 /* Remove the name from files[i]. Move further entries
1936 * down. When the array becomes empty free it here, since
1937 * FreeWild() won't be called below. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001938 vim_free(files[i]);
Bram Moolenaar9439cdd2009-04-22 13:39:36 +00001939 if (--num_files == 0)
1940 vim_free(files);
1941 else
1942 for ( ; i < num_files; ++i)
1943 files[i] = files[i + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00001944 }
1945 }
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00001946 if (nr > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001947 {
1948 file_count += num_files;
1949 if (nr <= file_count)
1950 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001951 *fname_out = vim_strsave(
1952 files[nr - 1 + num_files - file_count]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001953 dirp = (char_u *)""; /* stop searching */
1954 }
1955 }
1956 else if (list)
1957 {
1958 if (dir_name[0] == '.' && dir_name[1] == NUL)
1959 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02001960 if (fname == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001961 msg_puts(_(" In current directory:\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001962 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001963 msg_puts(_(" Using specified name:\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001964 }
1965 else
1966 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001967 msg_puts(_(" In directory "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001968 msg_home_replace(dir_name);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001969 msg_puts(":\n");
Bram Moolenaar071d4272004-06-13 20:20:40 +00001970 }
1971
1972 if (num_files)
1973 {
1974 for (i = 0; i < num_files; ++i)
1975 {
1976 /* print the swap file name */
1977 msg_outnum((long)++file_count);
Bram Moolenaar32526b32019-01-19 17:43:09 +01001978 msg_puts(". ");
1979 msg_puts((char *)gettail(files[i]));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001980 msg_putchar('\n');
1981 (void)swapfile_info(files[i]);
1982 }
1983 }
1984 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001985 msg_puts(_(" -- none --\n"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001986 out_flush();
1987 }
1988 else
1989 file_count += num_files;
1990
1991 for (i = 0; i < num_names; ++i)
1992 vim_free(names[i]);
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00001993 if (num_files > 0)
1994 FreeWild(num_files, files);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001995 }
1996 vim_free(dir_name);
1997 return file_count;
1998}
1999
Bram Moolenaar4f974752019-02-17 17:44:42 +01002000#if defined(UNIX) || defined(MSWIN) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002001/*
Bram Moolenaarb782ba42018-08-07 21:39:28 +02002002 * Need _very_ long file names.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002003 * Append the full path to name with path separators made into percent
2004 * signs, to dir. An unnamed buffer is handled as "" (<currentdir>/"")
2005 */
Bram Moolenaarb782ba42018-08-07 21:39:28 +02002006 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002007make_percent_swname(char_u *dir, char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002008{
Bram Moolenaarb782ba42018-08-07 21:39:28 +02002009 char_u *d = NULL, *s, *f;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002010
Bram Moolenaarb782ba42018-08-07 21:39:28 +02002011 f = fix_fname(name != NULL ? name : (char_u *)"");
Bram Moolenaar071d4272004-06-13 20:20:40 +00002012 if (f != NULL)
2013 {
2014 s = alloc((unsigned)(STRLEN(f) + 1));
2015 if (s != NULL)
2016 {
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002017 STRCPY(s, f);
Bram Moolenaar91acfff2017-03-12 19:22:36 +01002018 for (d = s; *d != NUL; MB_PTR_ADV(d))
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002019 if (vim_ispathsep(*d))
2020 *d = '%';
Bram Moolenaar071d4272004-06-13 20:20:40 +00002021 d = concat_fnames(dir, s, TRUE);
2022 vim_free(s);
2023 }
2024 vim_free(f);
2025 }
2026 return d;
2027}
2028#endif
2029
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02002030#if (defined(UNIX) || defined(VMS) || defined(MSWIN)) \
2031 && (defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG))
2032# define HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar071d4272004-06-13 20:20:40 +00002033static int process_still_running;
2034#endif
2035
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002036#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002037/*
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002038 * Return information found in swapfile "fname" in dictionary "d".
2039 * This is used by the swapinfo() function.
2040 */
2041 void
2042get_b0_dict(char_u *fname, dict_T *d)
2043{
2044 int fd;
2045 struct block0 b0;
2046
2047 if ((fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0)) >= 0)
2048 {
2049 if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0))
2050 {
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002051 if (ml_check_b0_id(&b0) == FAIL)
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002052 dict_add_string(d, "error", (char_u *)"Not a swap file");
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002053 else if (b0_magic_wrong(&b0))
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002054 dict_add_string(d, "error", (char_u *)"Magic number mismatch");
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002055 else
2056 {
2057 /* we have swap information */
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002058 dict_add_string_len(d, "version", b0.b0_version, 10);
2059 dict_add_string_len(d, "user", b0.b0_uname, B0_UNAME_SIZE);
2060 dict_add_string_len(d, "host", b0.b0_hname, B0_HNAME_SIZE);
2061 dict_add_string_len(d, "fname", b0.b0_fname, B0_FNAME_SIZE_ORG);
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002062
2063 dict_add_number(d, "pid", char_to_long(b0.b0_pid));
2064 dict_add_number(d, "mtime", char_to_long(b0.b0_mtime));
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002065 dict_add_number(d, "dirty", b0.b0_dirty ? 1 : 0);
2066# ifdef CHECK_INODE
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002067 dict_add_number(d, "inode", char_to_long(b0.b0_ino));
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002068# endif
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002069 }
2070 }
2071 else
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002072 dict_add_string(d, "error", (char_u *)"Cannot read file");
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002073 close(fd);
2074 }
2075 else
Bram Moolenaare6fdf792018-12-26 22:57:42 +01002076 dict_add_string(d, "error", (char_u *)"Cannot open file");
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002077}
Bram Moolenaar47ad5652018-08-21 21:09:07 +02002078#endif
Bram Moolenaar00f123a2018-08-21 20:28:54 +02002079
2080/*
Bram Moolenaar63d25552019-05-10 21:28:38 +02002081 * Replacement for ctime(), which is not safe to use.
2082 * Requires strftime(), otherwise returns "(unknown)".
2083 * If "thetime" is invalid returns "(invalid)". Never returns NULL.
2084 * When "add_newline" is TRUE add a newline like ctime() does.
2085 * Uses a static buffer.
2086 */
2087 char *
2088get_ctime(time_t thetime, int add_newline)
2089{
2090 static char buf[50];
2091#ifdef HAVE_STRFTIME
2092# ifdef HAVE_LOCALTIME_R
2093 struct tm tmval;
2094# endif
2095 struct tm *curtime;
2096
2097# ifdef HAVE_LOCALTIME_R
2098 curtime = localtime_r(&thetime, &tmval);
2099# else
2100 curtime = localtime(&thetime);
2101# endif
2102 /* MSVC returns NULL for an invalid value of seconds. */
2103 if (curtime == NULL)
2104 STRCPY(buf, _("(Invalid)"));
2105 else
2106 (void)strftime(buf, sizeof(buf) - 1, "%a %b %d %H:%M:%S %Y", curtime);
2107#else
2108 STRCPY(buf, "(unknown)");
2109#endif
2110 if (add_newline)
2111 STRCAT(buf, "\n");
2112 return buf;
2113}
2114
2115/*
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00002116 * Give information about an existing swap file.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002117 * Returns timestamp (0 when unknown).
2118 */
2119 static time_t
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002120swapfile_info(char_u *fname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002121{
Bram Moolenaar8767f522016-07-01 17:17:39 +02002122 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002123 int fd;
2124 struct block0 b0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002125#ifdef UNIX
2126 char_u uname[B0_UNAME_SIZE];
2127#endif
2128
Bram Moolenaar63d25552019-05-10 21:28:38 +02002129 // print the swap file date
Bram Moolenaar071d4272004-06-13 20:20:40 +00002130 if (mch_stat((char *)fname, &st) != -1)
2131 {
2132#ifdef UNIX
Bram Moolenaar63d25552019-05-10 21:28:38 +02002133 // print name of owner of the file
Bram Moolenaar071d4272004-06-13 20:20:40 +00002134 if (mch_get_uname(st.st_uid, uname, B0_UNAME_SIZE) == OK)
2135 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002136 msg_puts(_(" owned by: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002137 msg_outtrans(uname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01002138 msg_puts(_(" dated: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002139 }
2140 else
2141#endif
Bram Moolenaar32526b32019-01-19 17:43:09 +01002142 msg_puts(_(" dated: "));
Bram Moolenaar63d25552019-05-10 21:28:38 +02002143 msg_puts(get_ctime(st.st_mtime, TRUE));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002144 }
Bram Moolenaar63d25552019-05-10 21:28:38 +02002145 else
2146 st.st_mtime = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002147
2148 /*
2149 * print the original file name
2150 */
2151 fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
2152 if (fd >= 0)
2153 {
Bram Moolenaar540fc6f2010-12-17 16:27:16 +01002154 if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002155 {
2156 if (STRNCMP(b0.b0_version, "VIM 3.0", 7) == 0)
2157 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002158 msg_puts(_(" [from Vim version 3.0]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002159 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02002160 else if (ml_check_b0_id(&b0) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002161 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002162 msg_puts(_(" [does not look like a Vim swap file]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002163 }
2164 else
2165 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002166 msg_puts(_(" file name: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002167 if (b0.b0_fname[0] == NUL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002168 msg_puts(_("[No Name]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002169 else
2170 msg_outtrans(b0.b0_fname);
2171
Bram Moolenaar32526b32019-01-19 17:43:09 +01002172 msg_puts(_("\n modified: "));
2173 msg_puts(b0.b0_dirty ? _("YES") : _("no"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002174
2175 if (*(b0.b0_uname) != NUL)
2176 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002177 msg_puts(_("\n user name: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002178 msg_outtrans(b0.b0_uname);
2179 }
2180
2181 if (*(b0.b0_hname) != NUL)
2182 {
2183 if (*(b0.b0_uname) != NUL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002184 msg_puts(_(" host name: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002185 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002186 msg_puts(_("\n host name: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002187 msg_outtrans(b0.b0_hname);
2188 }
2189
2190 if (char_to_long(b0.b0_pid) != 0L)
2191 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002192 msg_puts(_("\n process ID: "));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002193 msg_outnum(char_to_long(b0.b0_pid));
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002194#if defined(UNIX) || defined(MSWIN)
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02002195 if (mch_process_running(char_to_long(b0.b0_pid)))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002196 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01002197 msg_puts(_(" (STILL RUNNING)"));
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02002198# ifdef HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar071d4272004-06-13 20:20:40 +00002199 process_still_running = TRUE;
2200# endif
2201 }
2202#endif
2203 }
2204
2205 if (b0_magic_wrong(&b0))
2206 {
Bram Moolenaar48e330a2016-02-23 14:53:34 +01002207#if defined(MSWIN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002208 if (STRNCMP(b0.b0_hname, "PC ", 3) == 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002209 msg_puts(_("\n [not usable with this version of Vim]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002210 else
2211#endif
Bram Moolenaar32526b32019-01-19 17:43:09 +01002212 msg_puts(_("\n [not usable on this computer]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002213 }
2214 }
2215 }
2216 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002217 msg_puts(_(" [cannot be read]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002218 close(fd);
2219 }
2220 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01002221 msg_puts(_(" [cannot be opened]"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002222 msg_putchar('\n');
2223
Bram Moolenaar63d25552019-05-10 21:28:38 +02002224 return st.st_mtime;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002225}
2226
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002227/*
2228 * Return TRUE if the swap file looks OK and there are no changes, thus it can
2229 * be safely deleted.
2230 */
2231 static time_t
2232swapfile_unchanged(char_u *fname)
2233{
2234 stat_T st;
2235 int fd;
2236 struct block0 b0;
2237 int ret = TRUE;
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02002238#if defined(UNIX) || defined(MSWIN)
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002239 long pid;
2240#endif
2241
2242 // must be able to stat the swap file
2243 if (mch_stat((char *)fname, &st) == -1)
2244 return FALSE;
2245
2246 // must be able to read the first block
2247 fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
2248 if (fd < 0)
2249 return FALSE;
2250 if (read_eintr(fd, &b0, sizeof(b0)) != sizeof(b0))
2251 {
2252 close(fd);
2253 return FALSE;
2254 }
2255
2256 // the ID and magic number must be correct
2257 if (ml_check_b0_id(&b0) == FAIL|| b0_magic_wrong(&b0))
2258 ret = FALSE;
2259
2260 // must be unchanged
2261 if (b0.b0_dirty)
2262 ret = FALSE;
2263
2264#if defined(UNIX) || defined(MSWIN)
2265 // process must known and not be running
2266 pid = char_to_long(b0.b0_pid);
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02002267 if (pid == 0L || mch_process_running(pid))
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02002268 ret = FALSE;
2269#endif
2270
2271 // TODO: Should we check if the swap file was created on the current
2272 // system? And the current user?
2273
2274 close(fd);
2275 return ret;
2276}
2277
Bram Moolenaar071d4272004-06-13 20:20:40 +00002278 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002279recov_file_names(char_u **names, char_u *path, int prepend_dot)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002280{
2281 int num_names;
2282
Bram Moolenaar071d4272004-06-13 20:20:40 +00002283 /*
2284 * (Win32 and Win64) never short names, but do prepend a dot.
2285 * (Not MS-DOS or Win32 or Win64) maybe short name, maybe not: Try both.
2286 * Only use the short name if it is different.
2287 */
2288 char_u *p;
2289 int i;
Bram Moolenaar4f974752019-02-17 17:44:42 +01002290# ifndef MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002291 int shortname = curbuf->b_shortname;
2292
2293 curbuf->b_shortname = FALSE;
2294# endif
2295
2296 num_names = 0;
2297
2298 /*
2299 * May also add the file name with a dot prepended, for swap file in same
2300 * dir as original file.
2301 */
2302 if (prepend_dot)
2303 {
2304 names[num_names] = modname(path, (char_u *)".sw?", TRUE);
2305 if (names[num_names] == NULL)
2306 goto end;
2307 ++num_names;
2308 }
2309
2310 /*
2311 * Form the normal swap file name pattern by appending ".sw?".
2312 */
2313#ifdef VMS
2314 names[num_names] = concat_fnames(path, (char_u *)"_sw%", FALSE);
2315#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002316 names[num_names] = concat_fnames(path, (char_u *)".sw?", FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002317#endif
2318 if (names[num_names] == NULL)
2319 goto end;
2320 if (num_names >= 1) /* check if we have the same name twice */
2321 {
2322 p = names[num_names - 1];
2323 i = (int)STRLEN(names[num_names - 1]) - (int)STRLEN(names[num_names]);
2324 if (i > 0)
2325 p += i; /* file name has been expanded to full path */
2326
2327 if (STRCMP(p, names[num_names]) != 0)
2328 ++num_names;
2329 else
2330 vim_free(names[num_names]);
2331 }
2332 else
2333 ++num_names;
2334
Bram Moolenaar4f974752019-02-17 17:44:42 +01002335# ifndef MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002336 /*
2337 * Also try with 'shortname' set, in case the file is on a DOS filesystem.
2338 */
2339 curbuf->b_shortname = TRUE;
2340#ifdef VMS
2341 names[num_names] = modname(path, (char_u *)"_sw%", FALSE);
2342#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002343 names[num_names] = modname(path, (char_u *)".sw?", FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002344#endif
2345 if (names[num_names] == NULL)
2346 goto end;
2347
2348 /*
2349 * Remove the one from 'shortname', if it's the same as with 'noshortname'.
2350 */
2351 p = names[num_names];
2352 i = STRLEN(names[num_names]) - STRLEN(names[num_names - 1]);
2353 if (i > 0)
2354 p += i; /* file name has been expanded to full path */
2355 if (STRCMP(names[num_names - 1], p) == 0)
2356 vim_free(names[num_names]);
2357 else
2358 ++num_names;
2359# endif
2360
2361end:
Bram Moolenaar4f974752019-02-17 17:44:42 +01002362# ifndef MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00002363 curbuf->b_shortname = shortname;
2364# endif
2365
Bram Moolenaar071d4272004-06-13 20:20:40 +00002366 return num_names;
2367}
2368
2369/*
2370 * sync all memlines
2371 *
2372 * If 'check_file' is TRUE, check if original file exists and was not changed.
2373 * If 'check_char' is TRUE, stop syncing when character becomes available, but
2374 * always sync at least one block.
2375 */
2376 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002377ml_sync_all(int check_file, int check_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002378{
2379 buf_T *buf;
Bram Moolenaar8767f522016-07-01 17:17:39 +02002380 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002381
Bram Moolenaar29323592016-07-24 22:04:11 +02002382 FOR_ALL_BUFFERS(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002383 {
2384 if (buf->b_ml.ml_mfp == NULL || buf->b_ml.ml_mfp->mf_fname == NULL)
2385 continue; /* no file */
2386
2387 ml_flush_line(buf); /* flush buffered line */
2388 /* flush locked block */
2389 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH);
2390 if (bufIsChanged(buf) && check_file && mf_need_trans(buf->b_ml.ml_mfp)
2391 && buf->b_ffname != NULL)
2392 {
2393 /*
2394 * If the original file does not exist anymore or has been changed
2395 * call ml_preserve() to get rid of all negative numbered blocks.
2396 */
2397 if (mch_stat((char *)buf->b_ffname, &st) == -1
2398 || st.st_mtime != buf->b_mtime_read
Bram Moolenaar914703b2010-05-31 21:59:46 +02002399 || st.st_size != buf->b_orig_size)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002400 {
2401 ml_preserve(buf, FALSE);
2402 did_check_timestamps = FALSE;
2403 need_check_timestamps = TRUE; /* give message later */
2404 }
2405 }
2406 if (buf->b_ml.ml_mfp->mf_dirty)
2407 {
2408 (void)mf_sync(buf->b_ml.ml_mfp, (check_char ? MFS_STOP : 0)
2409 | (bufIsChanged(buf) ? MFS_FLUSH : 0));
2410 if (check_char && ui_char_avail()) /* character available now */
2411 break;
2412 }
2413 }
2414}
2415
2416/*
2417 * sync one buffer, including negative blocks
2418 *
2419 * after this all the blocks are in the swap file
2420 *
2421 * Used for the :preserve command and when the original file has been
2422 * changed or deleted.
2423 *
2424 * when message is TRUE the success of preserving is reported
2425 */
2426 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002427ml_preserve(buf_T *buf, int message)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002428{
2429 bhdr_T *hp;
2430 linenr_T lnum;
2431 memfile_T *mfp = buf->b_ml.ml_mfp;
2432 int status;
2433 int got_int_save = got_int;
2434
2435 if (mfp == NULL || mfp->mf_fname == NULL)
2436 {
2437 if (message)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002438 emsg(_("E313: Cannot preserve, there is no swap file"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002439 return;
2440 }
2441
2442 /* We only want to stop when interrupted here, not when interrupted
2443 * before. */
2444 got_int = FALSE;
2445
2446 ml_flush_line(buf); /* flush buffered line */
2447 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); /* flush locked block */
2448 status = mf_sync(mfp, MFS_ALL | MFS_FLUSH);
2449
2450 /* stack is invalid after mf_sync(.., MFS_ALL) */
2451 buf->b_ml.ml_stack_top = 0;
2452
2453 /*
2454 * Some of the data blocks may have been changed from negative to
2455 * positive block number. In that case the pointer blocks need to be
2456 * updated.
2457 *
2458 * We don't know in which pointer block the references are, so we visit
2459 * all data blocks until there are no more translations to be done (or
2460 * we hit the end of the file, which can only happen in case a write fails,
2461 * e.g. when file system if full).
2462 * ml_find_line() does the work by translating the negative block numbers
2463 * when getting the first line of each data block.
2464 */
2465 if (mf_need_trans(mfp) && !got_int)
2466 {
2467 lnum = 1;
2468 while (mf_need_trans(mfp) && lnum <= buf->b_ml.ml_line_count)
2469 {
2470 hp = ml_find_line(buf, lnum, ML_FIND);
2471 if (hp == NULL)
2472 {
2473 status = FAIL;
2474 goto theend;
2475 }
2476 CHECK(buf->b_ml.ml_locked_low != lnum, "low != lnum");
2477 lnum = buf->b_ml.ml_locked_high + 1;
2478 }
2479 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); /* flush locked block */
2480 /* sync the updated pointer blocks */
2481 if (mf_sync(mfp, MFS_ALL | MFS_FLUSH) == FAIL)
2482 status = FAIL;
2483 buf->b_ml.ml_stack_top = 0; /* stack is invalid now */
2484 }
2485theend:
2486 got_int |= got_int_save;
2487
2488 if (message)
2489 {
2490 if (status == OK)
Bram Moolenaar32526b32019-01-19 17:43:09 +01002491 msg(_("File preserved"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002492 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002493 emsg(_("E314: Preserve failed"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002494 }
2495}
2496
2497/*
2498 * NOTE: The pointer returned by the ml_get_*() functions only remains valid
2499 * until the next call!
2500 * line1 = ml_get(1);
2501 * line2 = ml_get(2); // line1 is now invalid!
2502 * Make a copy of the line if necessary.
2503 */
2504/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002505 * Return a pointer to a (read-only copy of a) line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002506 *
2507 * On failure an error message is given and IObuff is returned (to avoid
2508 * having to check for error everywhere).
2509 */
2510 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002511ml_get(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002512{
2513 return ml_get_buf(curbuf, lnum, FALSE);
2514}
2515
2516/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002517 * Return pointer to position "pos".
Bram Moolenaar071d4272004-06-13 20:20:40 +00002518 */
2519 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002520ml_get_pos(pos_T *pos)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002521{
2522 return (ml_get_buf(curbuf, pos->lnum, FALSE) + pos->col);
2523}
2524
2525/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002526 * Return pointer to cursor line.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002527 */
2528 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002529ml_get_curline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002530{
2531 return ml_get_buf(curbuf, curwin->w_cursor.lnum, FALSE);
2532}
2533
2534/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002535 * Return pointer to cursor position.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002536 */
2537 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002538ml_get_cursor(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002539{
2540 return (ml_get_buf(curbuf, curwin->w_cursor.lnum, FALSE) +
2541 curwin->w_cursor.col);
2542}
2543
2544/*
Bram Moolenaar2e2e13c2010-12-08 13:17:03 +01002545 * Return a pointer to a line in a specific buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +00002546 *
2547 * "will_change": if TRUE mark the buffer dirty (chars in the line will be
2548 * changed)
2549 */
2550 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002551ml_get_buf(
2552 buf_T *buf,
2553 linenr_T lnum,
2554 int will_change) /* line will be changed */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002555{
Bram Moolenaarad40f022007-02-13 03:01:39 +00002556 bhdr_T *hp;
2557 DATA_BL *dp;
Bram Moolenaarad40f022007-02-13 03:01:39 +00002558 static int recursive = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002559
2560 if (lnum > buf->b_ml.ml_line_count) /* invalid line number */
2561 {
Bram Moolenaarad40f022007-02-13 03:01:39 +00002562 if (recursive == 0)
2563 {
2564 /* Avoid giving this message for a recursive call, may happen when
2565 * the GUI redraws part of the text. */
2566 ++recursive;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002567 siemsg(_("E315: ml_get: invalid lnum: %ld"), lnum);
Bram Moolenaarad40f022007-02-13 03:01:39 +00002568 --recursive;
2569 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002570errorret:
2571 STRCPY(IObuff, "???");
2572 return IObuff;
2573 }
2574 if (lnum <= 0) /* pretend line 0 is line 1 */
2575 lnum = 1;
2576
2577 if (buf->b_ml.ml_mfp == NULL) /* there are no lines */
2578 return (char_u *)"";
2579
Bram Moolenaar37d619f2010-03-10 14:46:26 +01002580 /*
2581 * See if it is the same line as requested last time.
2582 * Otherwise may need to flush last used line.
2583 * Don't use the last used line when 'swapfile' is reset, need to load all
2584 * blocks.
2585 */
Bram Moolenaar47b8b152007-02-07 02:41:57 +00002586 if (buf->b_ml.ml_line_lnum != lnum || mf_dont_release)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002587 {
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002588 unsigned start, end;
2589 colnr_T len;
2590 int idx;
2591
Bram Moolenaar071d4272004-06-13 20:20:40 +00002592 ml_flush_line(buf);
2593
2594 /*
2595 * Find the data block containing the line.
2596 * This also fills the stack with the blocks from the root to the data
2597 * block and releases any locked block.
2598 */
2599 if ((hp = ml_find_line(buf, lnum, ML_FIND)) == NULL)
2600 {
Bram Moolenaarad40f022007-02-13 03:01:39 +00002601 if (recursive == 0)
2602 {
2603 /* Avoid giving this message for a recursive call, may happen
2604 * when the GUI redraws part of the text. */
2605 ++recursive;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002606 siemsg(_("E316: ml_get: cannot find line %ld"), lnum);
Bram Moolenaarad40f022007-02-13 03:01:39 +00002607 --recursive;
2608 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002609 goto errorret;
2610 }
2611
2612 dp = (DATA_BL *)(hp->bh_data);
2613
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002614 idx = lnum - buf->b_ml.ml_locked_low;
2615 start = ((dp->db_index[idx]) & DB_INDEX_MASK);
2616 // The text ends where the previous line starts. The first line ends
2617 // at the end of the block.
2618 if (idx == 0)
2619 end = dp->db_txt_end;
2620 else
2621 end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK);
2622 len = end - start;
2623
2624 buf->b_ml.ml_line_ptr = (char_u *)dp + start;
2625 buf->b_ml.ml_line_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002626 buf->b_ml.ml_line_lnum = lnum;
2627 buf->b_ml.ml_flags &= ~ML_LINE_DIRTY;
2628 }
2629 if (will_change)
2630 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
2631
2632 return buf->b_ml.ml_line_ptr;
2633}
2634
2635/*
2636 * Check if a line that was just obtained by a call to ml_get
2637 * is in allocated memory.
2638 */
2639 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002640ml_line_alloced(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002641{
2642 return (curbuf->b_ml.ml_flags & ML_LINE_DIRTY);
2643}
2644
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002645#ifdef FEAT_TEXT_PROP
2646 static void
2647add_text_props_for_append(
2648 buf_T *buf,
2649 linenr_T lnum,
2650 char_u **line,
2651 int *len,
2652 char_u **tofree)
2653{
2654 int round;
2655 int new_prop_count = 0;
2656 int count;
2657 int n;
2658 char_u *props;
2659 int new_len;
2660 char_u *new_line;
2661 textprop_T prop;
2662
2663 // Make two rounds:
2664 // 1. calculate the extra space needed
2665 // 2. allocate the space and fill it
2666 for (round = 1; round <= 2; ++round)
2667 {
2668 if (round == 2)
2669 {
2670 if (new_prop_count == 0)
2671 return; // nothing to do
2672 new_len = *len + new_prop_count * sizeof(textprop_T);
2673 new_line = alloc((unsigned)new_len);
2674 if (new_line == NULL)
2675 return;
2676 mch_memmove(new_line, *line, *len);
2677 new_prop_count = 0;
2678 }
2679
2680 // Get the line above to find any props that continue in the next
2681 // line.
2682 count = get_text_props(buf, lnum, &props, FALSE);
2683 for (n = 0; n < count; ++n)
2684 {
2685 mch_memmove(&prop, props + n * sizeof(textprop_T), sizeof(textprop_T));
2686 if (prop.tp_flags & TP_FLAG_CONT_NEXT)
2687 {
2688 if (round == 2)
2689 {
2690 prop.tp_flags |= TP_FLAG_CONT_PREV;
2691 prop.tp_col = 1;
2692 prop.tp_len = *len;
2693 mch_memmove(new_line + *len + new_prop_count * sizeof(textprop_T), &prop, sizeof(textprop_T));
2694 }
2695 ++new_prop_count;
2696 }
2697 }
2698 }
2699 *line = new_line;
2700 *tofree = new_line;
2701 *len = new_len;
2702}
2703#endif
2704
Bram Moolenaar071d4272004-06-13 20:20:40 +00002705/*
2706 * Append a line after lnum (may be 0 to insert a line in front of the file).
2707 * "line" does not need to be allocated, but can't be another line in a
2708 * buffer, unlocking may make it invalid.
2709 *
2710 * newfile: TRUE when starting to edit a new file, meaning that pe_old_lnum
2711 * will be set for recovery
2712 * Check: The caller of this function should probably also call
2713 * appended_lines().
2714 *
2715 * return FAIL for failure, OK otherwise
2716 */
2717 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002718ml_append(
2719 linenr_T lnum, /* append after this line (can be 0) */
2720 char_u *line, /* text of the new line */
2721 colnr_T len, /* length of new line, including NUL, or 0 */
2722 int newfile) /* flag, see above */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002723{
2724 /* When starting up, we might still need to create the memfile */
Bram Moolenaar59f931e2010-07-24 20:27:03 +02002725 if (curbuf->b_ml.ml_mfp == NULL && open_buffer(FALSE, NULL, 0) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002726 return FAIL;
2727
2728 if (curbuf->b_ml.ml_line_lnum != 0)
2729 ml_flush_line(curbuf);
2730 return ml_append_int(curbuf, lnum, line, len, newfile, FALSE);
2731}
2732
Bram Moolenaar4033c552017-09-16 20:54:51 +02002733#if defined(FEAT_SPELL) || defined(FEAT_QUICKFIX) || defined(PROTO)
Bram Moolenaar4770d092006-01-12 23:22:24 +00002734/*
2735 * Like ml_append() but for an arbitrary buffer. The buffer must already have
2736 * a memline.
2737 */
2738 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002739ml_append_buf(
2740 buf_T *buf,
2741 linenr_T lnum, /* append after this line (can be 0) */
2742 char_u *line, /* text of the new line */
2743 colnr_T len, /* length of new line, including NUL, or 0 */
2744 int newfile) /* flag, see above */
Bram Moolenaar4770d092006-01-12 23:22:24 +00002745{
2746 if (buf->b_ml.ml_mfp == NULL)
2747 return FAIL;
2748
2749 if (buf->b_ml.ml_line_lnum != 0)
2750 ml_flush_line(buf);
2751 return ml_append_int(buf, lnum, line, len, newfile, FALSE);
2752}
2753#endif
2754
Bram Moolenaar071d4272004-06-13 20:20:40 +00002755 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01002756ml_append_int(
2757 buf_T *buf,
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002758 linenr_T lnum, // append after this line (can be 0)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002759 char_u *line_arg, // text of the new line
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002760 colnr_T len_arg, // length of line, including NUL, or 0
2761 int newfile, // flag, see above
2762 int mark) // mark the new line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002763{
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002764 char_u *line = line_arg;
2765 colnr_T len = len_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002766 int i;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002767 int line_count; // number of indexes in current block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002768 int offset;
2769 int from, to;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002770 int space_needed; // space needed for new line
Bram Moolenaar071d4272004-06-13 20:20:40 +00002771 int page_size;
2772 int page_count;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002773 int db_idx; // index for lnum in data block
Bram Moolenaar071d4272004-06-13 20:20:40 +00002774 bhdr_T *hp;
2775 memfile_T *mfp;
2776 DATA_BL *dp;
2777 PTR_BL *pp;
2778 infoptr_T *ip;
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002779#ifdef FEAT_TEXT_PROP
2780 char_u *tofree = NULL;
2781#endif
2782 int ret = FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002783
Bram Moolenaar071d4272004-06-13 20:20:40 +00002784 if (lnum > buf->b_ml.ml_line_count || buf->b_ml.ml_mfp == NULL)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002785 return FAIL; // lnum out of range
Bram Moolenaar071d4272004-06-13 20:20:40 +00002786
2787 if (lowest_marked && lowest_marked > lnum)
2788 lowest_marked = lnum + 1;
2789
2790 if (len == 0)
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002791 len = (colnr_T)STRLEN(line) + 1; // space needed for the text
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002792
2793#ifdef FEAT_TEXT_PROP
2794 if (curbuf->b_has_textprop && lnum > 0)
2795 // Add text properties that continue from the previous line.
2796 add_text_props_for_append(buf, lnum, &line, &len, &tofree);
2797#endif
2798
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002799 space_needed = len + INDEX_SIZE; // space needed for text + index
Bram Moolenaar071d4272004-06-13 20:20:40 +00002800
2801 mfp = buf->b_ml.ml_mfp;
2802 page_size = mfp->mf_page_size;
2803
2804/*
2805 * find the data block containing the previous line
2806 * This also fills the stack with the blocks from the root to the data block
2807 * This also releases any locked block.
2808 */
2809 if ((hp = ml_find_line(buf, lnum == 0 ? (linenr_T)1 : lnum,
2810 ML_INSERT)) == NULL)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002811 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002812
2813 buf->b_ml.ml_flags &= ~ML_EMPTY;
2814
2815 if (lnum == 0) /* got line one instead, correct db_idx */
2816 db_idx = -1; /* careful, it is negative! */
2817 else
2818 db_idx = lnum - buf->b_ml.ml_locked_low;
2819 /* get line count before the insertion */
2820 line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low;
2821
2822 dp = (DATA_BL *)(hp->bh_data);
2823
2824/*
2825 * If
2826 * - there is not enough room in the current block
2827 * - appending to the last line in the block
2828 * - not appending to the last line in the file
2829 * insert in front of the next block.
2830 */
2831 if ((int)dp->db_free < space_needed && db_idx == line_count - 1
2832 && lnum < buf->b_ml.ml_line_count)
2833 {
2834 /*
2835 * Now that the line is not going to be inserted in the block that we
2836 * expected, the line count has to be adjusted in the pointer blocks
2837 * by using ml_locked_lineadd.
2838 */
2839 --(buf->b_ml.ml_locked_lineadd);
2840 --(buf->b_ml.ml_locked_high);
2841 if ((hp = ml_find_line(buf, lnum + 1, ML_INSERT)) == NULL)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002842 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002843
2844 db_idx = -1; /* careful, it is negative! */
2845 /* get line count before the insertion */
2846 line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low;
2847 CHECK(buf->b_ml.ml_locked_low != lnum + 1, "locked_low != lnum + 1");
2848
2849 dp = (DATA_BL *)(hp->bh_data);
2850 }
2851
2852 ++buf->b_ml.ml_line_count;
2853
2854 if ((int)dp->db_free >= space_needed) /* enough room in data block */
2855 {
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002856 /*
2857 * Insert the new line in an existing data block, or in the data block
2858 * allocated above.
2859 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002860 dp->db_txt_start -= len;
2861 dp->db_free -= space_needed;
2862 ++(dp->db_line_count);
2863
2864 /*
2865 * move the text of the lines that follow to the front
2866 * adjust the indexes of the lines that follow
2867 */
2868 if (line_count > db_idx + 1) /* if there are following lines */
2869 {
2870 /*
2871 * Offset is the start of the previous line.
2872 * This will become the character just after the new line.
2873 */
2874 if (db_idx < 0)
2875 offset = dp->db_txt_end;
2876 else
2877 offset = ((dp->db_index[db_idx]) & DB_INDEX_MASK);
2878 mch_memmove((char *)dp + dp->db_txt_start,
2879 (char *)dp + dp->db_txt_start + len,
2880 (size_t)(offset - (dp->db_txt_start + len)));
2881 for (i = line_count - 1; i > db_idx; --i)
2882 dp->db_index[i + 1] = dp->db_index[i] - len;
2883 dp->db_index[db_idx + 1] = offset - len;
2884 }
Bram Moolenaar98aefe72018-12-13 22:20:09 +01002885 else
2886 // add line at the end (which is the start of the text)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002887 dp->db_index[db_idx + 1] = dp->db_txt_start;
2888
2889 /*
2890 * copy the text into the block
2891 */
2892 mch_memmove((char *)dp + dp->db_index[db_idx + 1], line, (size_t)len);
2893 if (mark)
2894 dp->db_index[db_idx + 1] |= DB_MARKED;
2895
2896 /*
2897 * Mark the block dirty.
2898 */
2899 buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
2900 if (!newfile)
2901 buf->b_ml.ml_flags |= ML_LOCKED_POS;
2902 }
2903 else /* not enough space in data block */
2904 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002905 long line_count_left, line_count_right;
2906 int page_count_left, page_count_right;
2907 bhdr_T *hp_left;
2908 bhdr_T *hp_right;
2909 bhdr_T *hp_new;
2910 int lines_moved;
2911 int data_moved = 0; /* init to shut up gcc */
2912 int total_moved = 0; /* init to shut up gcc */
2913 DATA_BL *dp_right, *dp_left;
2914 int stack_idx;
2915 int in_left;
2916 int lineadd;
2917 blocknr_T bnum_left, bnum_right;
2918 linenr_T lnum_left, lnum_right;
2919 int pb_idx;
2920 PTR_BL *pp_new;
2921
2922 /*
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002923 * There is not enough room, we have to create a new data block and
2924 * copy some lines into it.
2925 * Then we have to insert an entry in the pointer block.
2926 * If this pointer block also is full, we go up another block, and so
2927 * on, up to the root if necessary.
2928 * The line counts in the pointer blocks have already been adjusted by
2929 * ml_find_line().
2930 *
Bram Moolenaar071d4272004-06-13 20:20:40 +00002931 * We are going to allocate a new data block. Depending on the
2932 * situation it will be put to the left or right of the existing
2933 * block. If possible we put the new line in the left block and move
2934 * the lines after it to the right block. Otherwise the new line is
2935 * also put in the right block. This method is more efficient when
2936 * inserting a lot of lines at one place.
2937 */
2938 if (db_idx < 0) /* left block is new, right block is existing */
2939 {
2940 lines_moved = 0;
2941 in_left = TRUE;
2942 /* space_needed does not change */
2943 }
2944 else /* left block is existing, right block is new */
2945 {
2946 lines_moved = line_count - db_idx - 1;
2947 if (lines_moved == 0)
2948 in_left = FALSE; /* put new line in right block */
2949 /* space_needed does not change */
2950 else
2951 {
2952 data_moved = ((dp->db_index[db_idx]) & DB_INDEX_MASK) -
2953 dp->db_txt_start;
2954 total_moved = data_moved + lines_moved * INDEX_SIZE;
2955 if ((int)dp->db_free + total_moved >= space_needed)
2956 {
2957 in_left = TRUE; /* put new line in left block */
2958 space_needed = total_moved;
2959 }
2960 else
2961 {
2962 in_left = FALSE; /* put new line in right block */
2963 space_needed += total_moved;
2964 }
2965 }
2966 }
2967
2968 page_count = ((space_needed + HEADER_SIZE) + page_size - 1) / page_size;
2969 if ((hp_new = ml_new_data(mfp, newfile, page_count)) == NULL)
2970 {
2971 /* correct line counts in pointer blocks */
2972 --(buf->b_ml.ml_locked_lineadd);
2973 --(buf->b_ml.ml_locked_high);
Bram Moolenaarb56ac042018-12-28 23:22:40 +01002974 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002975 }
2976 if (db_idx < 0) /* left block is new */
2977 {
2978 hp_left = hp_new;
2979 hp_right = hp;
2980 line_count_left = 0;
2981 line_count_right = line_count;
2982 }
2983 else /* right block is new */
2984 {
2985 hp_left = hp;
2986 hp_right = hp_new;
2987 line_count_left = line_count;
2988 line_count_right = 0;
2989 }
2990 dp_right = (DATA_BL *)(hp_right->bh_data);
2991 dp_left = (DATA_BL *)(hp_left->bh_data);
2992 bnum_left = hp_left->bh_bnum;
2993 bnum_right = hp_right->bh_bnum;
2994 page_count_left = hp_left->bh_page_count;
2995 page_count_right = hp_right->bh_page_count;
2996
2997 /*
2998 * May move the new line into the right/new block.
2999 */
3000 if (!in_left)
3001 {
3002 dp_right->db_txt_start -= len;
3003 dp_right->db_free -= len + INDEX_SIZE;
3004 dp_right->db_index[0] = dp_right->db_txt_start;
3005 if (mark)
3006 dp_right->db_index[0] |= DB_MARKED;
3007
3008 mch_memmove((char *)dp_right + dp_right->db_txt_start,
3009 line, (size_t)len);
3010 ++line_count_right;
3011 }
3012 /*
3013 * may move lines from the left/old block to the right/new one.
3014 */
3015 if (lines_moved)
3016 {
3017 /*
3018 */
3019 dp_right->db_txt_start -= data_moved;
3020 dp_right->db_free -= total_moved;
3021 mch_memmove((char *)dp_right + dp_right->db_txt_start,
3022 (char *)dp_left + dp_left->db_txt_start,
3023 (size_t)data_moved);
3024 offset = dp_right->db_txt_start - dp_left->db_txt_start;
3025 dp_left->db_txt_start += data_moved;
3026 dp_left->db_free += total_moved;
3027
3028 /*
3029 * update indexes in the new block
3030 */
3031 for (to = line_count_right, from = db_idx + 1;
3032 from < line_count_left; ++from, ++to)
3033 dp_right->db_index[to] = dp->db_index[from] + offset;
3034 line_count_right += lines_moved;
3035 line_count_left -= lines_moved;
3036 }
3037
3038 /*
3039 * May move the new line into the left (old or new) block.
3040 */
3041 if (in_left)
3042 {
3043 dp_left->db_txt_start -= len;
3044 dp_left->db_free -= len + INDEX_SIZE;
3045 dp_left->db_index[line_count_left] = dp_left->db_txt_start;
3046 if (mark)
3047 dp_left->db_index[line_count_left] |= DB_MARKED;
3048 mch_memmove((char *)dp_left + dp_left->db_txt_start,
3049 line, (size_t)len);
3050 ++line_count_left;
3051 }
3052
3053 if (db_idx < 0) /* left block is new */
3054 {
3055 lnum_left = lnum + 1;
3056 lnum_right = 0;
3057 }
3058 else /* right block is new */
3059 {
3060 lnum_left = 0;
3061 if (in_left)
3062 lnum_right = lnum + 2;
3063 else
3064 lnum_right = lnum + 1;
3065 }
3066 dp_left->db_line_count = line_count_left;
3067 dp_right->db_line_count = line_count_right;
3068
3069 /*
3070 * release the two data blocks
3071 * The new one (hp_new) already has a correct blocknumber.
3072 * The old one (hp, in ml_locked) gets a positive blocknumber if
3073 * we changed it and we are not editing a new file.
3074 */
3075 if (lines_moved || in_left)
3076 buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
3077 if (!newfile && db_idx >= 0 && in_left)
3078 buf->b_ml.ml_flags |= ML_LOCKED_POS;
3079 mf_put(mfp, hp_new, TRUE, FALSE);
3080
3081 /*
3082 * flush the old data block
3083 * set ml_locked_lineadd to 0, because the updating of the
3084 * pointer blocks is done below
3085 */
3086 lineadd = buf->b_ml.ml_locked_lineadd;
3087 buf->b_ml.ml_locked_lineadd = 0;
3088 ml_find_line(buf, (linenr_T)0, ML_FLUSH); /* flush data block */
3089
3090 /*
3091 * update pointer blocks for the new data block
3092 */
3093 for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0;
3094 --stack_idx)
3095 {
3096 ip = &(buf->b_ml.ml_stack[stack_idx]);
3097 pb_idx = ip->ip_index;
3098 if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003099 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003100 pp = (PTR_BL *)(hp->bh_data); /* must be pointer block */
3101 if (pp->pb_id != PTR_ID)
3102 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003103 iemsg(_("E317: pointer block id wrong 3"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003104 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003105 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003106 }
3107 /*
3108 * TODO: If the pointer block is full and we are adding at the end
3109 * try to insert in front of the next block
3110 */
3111 /* block not full, add one entry */
3112 if (pp->pb_count < pp->pb_count_max)
3113 {
3114 if (pb_idx + 1 < (int)pp->pb_count)
3115 mch_memmove(&pp->pb_pointer[pb_idx + 2],
3116 &pp->pb_pointer[pb_idx + 1],
3117 (size_t)(pp->pb_count - pb_idx - 1) * sizeof(PTR_EN));
3118 ++pp->pb_count;
3119 pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
3120 pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
3121 pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
3122 pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right;
3123 pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right;
3124 pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right;
3125
3126 if (lnum_left != 0)
3127 pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left;
3128 if (lnum_right != 0)
3129 pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right;
3130
3131 mf_put(mfp, hp, TRUE, FALSE);
3132 buf->b_ml.ml_stack_top = stack_idx + 1; /* truncate stack */
3133
3134 if (lineadd)
3135 {
3136 --(buf->b_ml.ml_stack_top);
Bram Moolenaar6b803a72007-05-06 14:25:46 +00003137 /* fix line count for rest of blocks in the stack */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003138 ml_lineadd(buf, lineadd);
3139 /* fix stack itself */
3140 buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high +=
3141 lineadd;
3142 ++(buf->b_ml.ml_stack_top);
3143 }
3144
3145 /*
3146 * We are finished, break the loop here.
3147 */
3148 break;
3149 }
3150 else /* pointer block full */
3151 {
3152 /*
3153 * split the pointer block
3154 * allocate a new pointer block
3155 * move some of the pointer into the new block
3156 * prepare for updating the parent block
3157 */
3158 for (;;) /* do this twice when splitting block 1 */
3159 {
3160 hp_new = ml_new_ptr(mfp);
3161 if (hp_new == NULL) /* TODO: try to fix tree */
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003162 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003163 pp_new = (PTR_BL *)(hp_new->bh_data);
3164
3165 if (hp->bh_bnum != 1)
3166 break;
3167
3168 /*
3169 * if block 1 becomes full the tree is given an extra level
3170 * The pointers from block 1 are moved into the new block.
3171 * block 1 is updated to point to the new block
3172 * then continue to split the new block
3173 */
3174 mch_memmove(pp_new, pp, (size_t)page_size);
3175 pp->pb_count = 1;
3176 pp->pb_pointer[0].pe_bnum = hp_new->bh_bnum;
3177 pp->pb_pointer[0].pe_line_count = buf->b_ml.ml_line_count;
3178 pp->pb_pointer[0].pe_old_lnum = 1;
3179 pp->pb_pointer[0].pe_page_count = 1;
3180 mf_put(mfp, hp, TRUE, FALSE); /* release block 1 */
3181 hp = hp_new; /* new block is to be split */
3182 pp = pp_new;
3183 CHECK(stack_idx != 0, _("stack_idx should be 0"));
3184 ip->ip_index = 0;
3185 ++stack_idx; /* do block 1 again later */
3186 }
3187 /*
3188 * move the pointers after the current one to the new block
3189 * If there are none, the new entry will be in the new block.
3190 */
3191 total_moved = pp->pb_count - pb_idx - 1;
3192 if (total_moved)
3193 {
3194 mch_memmove(&pp_new->pb_pointer[0],
3195 &pp->pb_pointer[pb_idx + 1],
3196 (size_t)(total_moved) * sizeof(PTR_EN));
3197 pp_new->pb_count = total_moved;
3198 pp->pb_count -= total_moved - 1;
3199 pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right;
3200 pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right;
3201 pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right;
3202 if (lnum_right)
3203 pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right;
3204 }
3205 else
3206 {
3207 pp_new->pb_count = 1;
3208 pp_new->pb_pointer[0].pe_bnum = bnum_right;
3209 pp_new->pb_pointer[0].pe_line_count = line_count_right;
3210 pp_new->pb_pointer[0].pe_page_count = page_count_right;
3211 pp_new->pb_pointer[0].pe_old_lnum = lnum_right;
3212 }
3213 pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
3214 pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
3215 pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
3216 if (lnum_left)
3217 pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left;
3218 lnum_left = 0;
3219 lnum_right = 0;
3220
3221 /*
3222 * recompute line counts
3223 */
3224 line_count_right = 0;
3225 for (i = 0; i < (int)pp_new->pb_count; ++i)
3226 line_count_right += pp_new->pb_pointer[i].pe_line_count;
3227 line_count_left = 0;
3228 for (i = 0; i < (int)pp->pb_count; ++i)
3229 line_count_left += pp->pb_pointer[i].pe_line_count;
3230
3231 bnum_left = hp->bh_bnum;
3232 bnum_right = hp_new->bh_bnum;
3233 page_count_left = 1;
3234 page_count_right = 1;
3235 mf_put(mfp, hp, TRUE, FALSE);
3236 mf_put(mfp, hp_new, TRUE, FALSE);
3237 }
3238 }
3239
3240 /*
3241 * Safety check: fallen out of for loop?
3242 */
3243 if (stack_idx < 0)
3244 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003245 iemsg(_("E318: Updated too many blocks?"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003246 buf->b_ml.ml_stack_top = 0; /* invalidate stack */
3247 }
3248 }
3249
3250#ifdef FEAT_BYTEOFF
3251 /* The line was inserted below 'lnum' */
3252 ml_updatechunk(buf, lnum + 1, (long)len, ML_CHNK_ADDLINE);
3253#endif
3254#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02003255 if (netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +00003256 {
3257 if (STRLEN(line) > 0)
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003258 netbeans_inserted(buf, lnum+1, (colnr_T)0, line, (int)STRLEN(line));
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00003259 netbeans_inserted(buf, lnum+1, (colnr_T)STRLEN(line),
Bram Moolenaar071d4272004-06-13 20:20:40 +00003260 (char_u *)"\n", 1);
3261 }
3262#endif
Bram Moolenaar509ce2a2016-03-11 22:52:15 +01003263#ifdef FEAT_JOB_CHANNEL
Bram Moolenaar99ef0622016-03-06 20:22:25 +01003264 if (buf->b_write_to_channel)
3265 channel_write_new_lines(buf);
3266#endif
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003267 ret = OK;
Bram Moolenaar99ef0622016-03-06 20:22:25 +01003268
Bram Moolenaarb56ac042018-12-28 23:22:40 +01003269theend:
3270#ifdef FEAT_TEXT_PROP
3271 vim_free(tofree);
3272#endif
3273 return ret;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003274}
3275
3276/*
Bram Moolenaar4770d092006-01-12 23:22:24 +00003277 * Replace line lnum, with buffering, in current buffer.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003278 *
Bram Moolenaar1056d982006-03-09 22:37:52 +00003279 * If "copy" is TRUE, make a copy of the line, otherwise the line has been
Bram Moolenaar071d4272004-06-13 20:20:40 +00003280 * copied to allocated memory already.
3281 *
3282 * Check: The caller of this function should probably also call
3283 * changed_lines(), unless update_screen(NOT_VALID) is used.
3284 *
3285 * return FAIL for failure, OK otherwise
3286 */
3287 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003288ml_replace(linenr_T lnum, char_u *line, int copy)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003289{
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003290 colnr_T len = -1;
3291
3292 if (line != NULL)
Bram Moolenaar4efe73b2018-12-16 14:37:39 +01003293 len = (colnr_T)STRLEN(line);
Bram Moolenaarccae4672019-01-04 15:09:57 +01003294 return ml_replace_len(lnum, line, len, FALSE, copy);
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003295}
3296
Bram Moolenaarccae4672019-01-04 15:09:57 +01003297/*
3298 * Replace a line for the current buffer. Like ml_replace() with:
3299 * "len_arg" is the length of the text, excluding NUL.
3300 * If "has_props" is TRUE then "line_arg" includes the text properties and
3301 * "len_arg" includes the NUL of the text.
3302 */
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003303 int
Bram Moolenaarccae4672019-01-04 15:09:57 +01003304ml_replace_len(
3305 linenr_T lnum,
3306 char_u *line_arg,
3307 colnr_T len_arg,
3308 int has_props,
3309 int copy)
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003310{
3311 char_u *line = line_arg;
3312 colnr_T len = len_arg;
3313
Bram Moolenaar071d4272004-06-13 20:20:40 +00003314 if (line == NULL) /* just checking... */
3315 return FAIL;
3316
3317 /* When starting up, we might still need to create the memfile */
Bram Moolenaar59f931e2010-07-24 20:27:03 +02003318 if (curbuf->b_ml.ml_mfp == NULL && open_buffer(FALSE, NULL, 0) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003319 return FAIL;
3320
Bram Moolenaarccae4672019-01-04 15:09:57 +01003321 if (!has_props)
3322 ++len; // include the NUL after the text
3323 if (copy)
3324 {
3325 // copy the line to allocated memory
3326#ifdef FEAT_TEXT_PROP
3327 if (has_props)
3328 line = vim_memsave(line, len);
3329 else
3330#endif
3331 line = vim_strnsave(line, len - 1);
3332 if (line == NULL)
3333 return FAIL;
3334 }
3335
Bram Moolenaar071d4272004-06-13 20:20:40 +00003336#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02003337 if (netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +00003338 {
3339 netbeans_removed(curbuf, lnum, 0, (long)STRLEN(ml_get(lnum)));
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003340 netbeans_inserted(curbuf, lnum, 0, line, (int)STRLEN(line));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003341 }
3342#endif
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003343 if (curbuf->b_ml.ml_line_lnum != lnum)
3344 {
3345 // another line is buffered, flush it
3346 ml_flush_line(curbuf);
Bram Moolenaarca79a5f2018-12-13 23:16:36 +01003347 curbuf->b_ml.ml_flags &= ~ML_LINE_DIRTY;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003348
3349#ifdef FEAT_TEXT_PROP
Bram Moolenaarccae4672019-01-04 15:09:57 +01003350 if (curbuf->b_has_textprop && !has_props)
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003351 // Need to fetch the old line to copy over any text properties.
3352 ml_get_buf(curbuf, lnum, TRUE);
3353#endif
3354 }
3355
3356#ifdef FEAT_TEXT_PROP
Bram Moolenaarccae4672019-01-04 15:09:57 +01003357 if (curbuf->b_has_textprop && !has_props)
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003358 {
3359 size_t oldtextlen = STRLEN(curbuf->b_ml.ml_line_ptr) + 1;
3360
3361 if (oldtextlen < (size_t)curbuf->b_ml.ml_line_len)
3362 {
3363 char_u *newline;
3364 size_t textproplen = curbuf->b_ml.ml_line_len - oldtextlen;
3365
3366 // Need to copy over text properties, stored after the text.
Bram Moolenaarccae4672019-01-04 15:09:57 +01003367 newline = alloc(len + (int)textproplen);
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003368 if (newline != NULL)
3369 {
Bram Moolenaarccae4672019-01-04 15:09:57 +01003370 mch_memmove(newline, line, len);
3371 mch_memmove(newline + len, curbuf->b_ml.ml_line_ptr + oldtextlen, textproplen);
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003372 vim_free(line);
3373 line = newline;
Bram Moolenaar4efe73b2018-12-16 14:37:39 +01003374 len += (colnr_T)textproplen;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003375 }
3376 }
3377 }
3378#endif
3379
Bram Moolenaarccae4672019-01-04 15:09:57 +01003380 if (curbuf->b_ml.ml_flags & ML_LINE_DIRTY) // same line allocated
3381 vim_free(curbuf->b_ml.ml_line_ptr); // free it
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003382
Bram Moolenaar071d4272004-06-13 20:20:40 +00003383 curbuf->b_ml.ml_line_ptr = line;
Bram Moolenaarccae4672019-01-04 15:09:57 +01003384 curbuf->b_ml.ml_line_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003385 curbuf->b_ml.ml_line_lnum = lnum;
3386 curbuf->b_ml.ml_flags = (curbuf->b_ml.ml_flags | ML_LINE_DIRTY) & ~ML_EMPTY;
3387
3388 return OK;
3389}
3390
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003391#ifdef FEAT_TEXT_PROP
3392/*
3393 * Adjust text properties in line "lnum" for a deleted line.
3394 * When "above" is true this is the line above the deleted line.
3395 * "del_props" are the properties of the deleted line.
3396 */
3397 static void
3398adjust_text_props_for_delete(
3399 buf_T *buf,
3400 linenr_T lnum,
3401 char_u *del_props,
3402 int del_props_len,
3403 int above)
3404{
3405 int did_get_line = FALSE;
3406 int done_del;
3407 int done_this;
3408 textprop_T prop_del;
3409 textprop_T prop_this;
3410 bhdr_T *hp;
3411 DATA_BL *dp;
3412 int idx;
3413 int line_start;
3414 long line_size;
3415 int this_props_len;
3416 char_u *text;
3417 size_t textlen;
3418 int found;
3419
3420 for (done_del = 0; done_del < del_props_len; done_del += sizeof(textprop_T))
3421 {
3422 mch_memmove(&prop_del, del_props + done_del, sizeof(textprop_T));
3423 if ((above && (prop_del.tp_flags & TP_FLAG_CONT_PREV)
3424 && !(prop_del.tp_flags & TP_FLAG_CONT_NEXT))
3425 || (!above && (prop_del.tp_flags & TP_FLAG_CONT_NEXT)
3426 && !(prop_del.tp_flags & TP_FLAG_CONT_PREV)))
3427 {
3428 if (!did_get_line)
3429 {
3430 did_get_line = TRUE;
3431 if ((hp = ml_find_line(buf, lnum, ML_FIND)) == NULL)
3432 return;
3433
3434 dp = (DATA_BL *)(hp->bh_data);
3435 idx = lnum - buf->b_ml.ml_locked_low;
3436 line_start = ((dp->db_index[idx]) & DB_INDEX_MASK);
3437 if (idx == 0) // first line in block, text at the end
3438 line_size = dp->db_txt_end - line_start;
3439 else
3440 line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK) - line_start;
3441 text = (char_u *)dp + line_start;
3442 textlen = STRLEN(text) + 1;
3443 if ((long)textlen >= line_size)
3444 {
3445 if (above)
3446 internal_error("no text property above deleted line");
3447 else
3448 internal_error("no text property below deleted line");
3449 return;
3450 }
Bram Moolenaar4b7214e2019-01-03 21:55:32 +01003451 this_props_len = line_size - (int)textlen;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003452 }
3453
3454 found = FALSE;
3455 for (done_this = 0; done_this < this_props_len; done_this += sizeof(textprop_T))
3456 {
3457 mch_memmove(&prop_this, text + textlen + done_del, sizeof(textprop_T));
3458 if (prop_del.tp_id == prop_this.tp_id
3459 && prop_del.tp_type == prop_this.tp_type)
3460 {
3461 int flag = above ? TP_FLAG_CONT_NEXT : TP_FLAG_CONT_PREV;
3462
3463 found = TRUE;
3464 if (prop_this.tp_flags & flag)
3465 {
3466 prop_this.tp_flags &= ~flag;
3467 mch_memmove(text + textlen + done_del, &prop_this, sizeof(textprop_T));
3468 }
3469 else if (above)
3470 internal_error("text property above deleted line does not continue");
3471 else
3472 internal_error("text property below deleted line does not continue");
3473 }
3474 }
3475 if (!found)
3476 {
3477 if (above)
3478 internal_error("text property above deleted line not found");
3479 else
3480 internal_error("text property below deleted line not found");
3481 }
3482
3483 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
3484 }
3485 }
3486}
3487#endif
3488
Bram Moolenaar071d4272004-06-13 20:20:40 +00003489/*
Bram Moolenaar4033c552017-09-16 20:54:51 +02003490 * Delete line "lnum" in the current buffer.
3491 * When "message" is TRUE may give a "No lines in buffer" message.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003492 *
3493 * Check: The caller of this function should probably also call
3494 * deleted_lines() after this.
3495 *
3496 * return FAIL for failure, OK otherwise
3497 */
3498 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003499ml_delete(linenr_T lnum, int message)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003500{
3501 ml_flush_line(curbuf);
3502 return ml_delete_int(curbuf, lnum, message);
3503}
3504
3505 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003506ml_delete_int(buf_T *buf, linenr_T lnum, int message)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003507{
3508 bhdr_T *hp;
3509 memfile_T *mfp;
3510 DATA_BL *dp;
3511 PTR_BL *pp;
3512 infoptr_T *ip;
3513 int count; /* number of entries in block */
3514 int idx;
3515 int stack_idx;
3516 int text_start;
3517 int line_start;
3518 long line_size;
3519 int i;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003520 int ret = FAIL;
3521#ifdef FEAT_TEXT_PROP
3522 char_u *textprop_save = NULL;
3523 int textprop_save_len;
3524#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003525
3526 if (lnum < 1 || lnum > buf->b_ml.ml_line_count)
3527 return FAIL;
3528
3529 if (lowest_marked && lowest_marked > lnum)
3530 lowest_marked--;
3531
3532/*
3533 * If the file becomes empty the last line is replaced by an empty line.
3534 */
3535 if (buf->b_ml.ml_line_count == 1) /* file becomes empty */
3536 {
3537 if (message
3538#ifdef FEAT_NETBEANS_INTG
3539 && !netbeansSuppressNoLines
3540#endif
3541 )
Bram Moolenaar238a5642006-02-21 22:12:05 +00003542 set_keep_msg((char_u *)_(no_lines_msg), 0);
3543
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02003544 /* FEAT_BYTEOFF already handled in there, don't worry 'bout it below */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003545 i = ml_replace((linenr_T)1, (char_u *)"", TRUE);
3546 buf->b_ml.ml_flags |= ML_EMPTY;
3547
3548 return i;
3549 }
3550
3551/*
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003552 * Find the data block containing the line.
3553 * This also fills the stack with the blocks from the root to the data block.
3554 * This also releases any locked block..
Bram Moolenaar071d4272004-06-13 20:20:40 +00003555 */
3556 mfp = buf->b_ml.ml_mfp;
3557 if (mfp == NULL)
3558 return FAIL;
3559
3560 if ((hp = ml_find_line(buf, lnum, ML_DELETE)) == NULL)
3561 return FAIL;
3562
3563 dp = (DATA_BL *)(hp->bh_data);
3564 /* compute line count before the delete */
3565 count = (long)(buf->b_ml.ml_locked_high)
3566 - (long)(buf->b_ml.ml_locked_low) + 2;
3567 idx = lnum - buf->b_ml.ml_locked_low;
3568
3569 --buf->b_ml.ml_line_count;
3570
3571 line_start = ((dp->db_index[idx]) & DB_INDEX_MASK);
3572 if (idx == 0) /* first line in block, text at the end */
3573 line_size = dp->db_txt_end - line_start;
3574 else
3575 line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK) - line_start;
3576
3577#ifdef FEAT_NETBEANS_INTG
Bram Moolenaarb26e6322010-05-22 21:34:09 +02003578 if (netbeans_active())
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00003579 netbeans_removed(buf, lnum, 0, (long)line_size);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003580#endif
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003581#ifdef FEAT_TEXT_PROP
3582 // If there are text properties, make a copy, so that we can update
3583 // properties in preceding and following lines.
3584 if (buf->b_has_textprop)
3585 {
3586 size_t textlen = STRLEN((char_u *)dp + line_start) + 1;
3587
3588 if ((long)textlen < line_size)
3589 {
Bram Moolenaar4b7214e2019-01-03 21:55:32 +01003590 textprop_save_len = line_size - (int)textlen;
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003591 textprop_save = vim_memsave((char_u *)dp + line_start + textlen,
3592 textprop_save_len);
3593 }
3594 }
3595#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003596
3597/*
3598 * special case: If there is only one line in the data block it becomes empty.
3599 * Then we have to remove the entry, pointing to this data block, from the
3600 * pointer block. If this pointer block also becomes empty, we go up another
3601 * block, and so on, up to the root if necessary.
3602 * The line counts in the pointer blocks have already been adjusted by
3603 * ml_find_line().
3604 */
3605 if (count == 1)
3606 {
3607 mf_free(mfp, hp); /* free the data block */
3608 buf->b_ml.ml_locked = NULL;
3609
Bram Moolenaare60acc12011-05-10 16:41:25 +02003610 for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0;
3611 --stack_idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003612 {
3613 buf->b_ml.ml_stack_top = 0; /* stack is invalid when failing */
3614 ip = &(buf->b_ml.ml_stack[stack_idx]);
3615 idx = ip->ip_index;
3616 if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003617 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003618 pp = (PTR_BL *)(hp->bh_data); /* must be pointer block */
3619 if (pp->pb_id != PTR_ID)
3620 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003621 iemsg(_("E317: pointer block id wrong 4"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003622 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003623 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003624 }
3625 count = --(pp->pb_count);
3626 if (count == 0) /* the pointer block becomes empty! */
3627 mf_free(mfp, hp);
3628 else
3629 {
3630 if (count != idx) /* move entries after the deleted one */
3631 mch_memmove(&pp->pb_pointer[idx], &pp->pb_pointer[idx + 1],
3632 (size_t)(count - idx) * sizeof(PTR_EN));
3633 mf_put(mfp, hp, TRUE, FALSE);
3634
3635 buf->b_ml.ml_stack_top = stack_idx; /* truncate stack */
Bram Moolenaar6b803a72007-05-06 14:25:46 +00003636 /* fix line count for rest of blocks in the stack */
3637 if (buf->b_ml.ml_locked_lineadd != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003638 {
3639 ml_lineadd(buf, buf->b_ml.ml_locked_lineadd);
3640 buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high +=
Bram Moolenaar6b803a72007-05-06 14:25:46 +00003641 buf->b_ml.ml_locked_lineadd;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003642 }
3643 ++(buf->b_ml.ml_stack_top);
3644
3645 break;
3646 }
3647 }
3648 CHECK(stack_idx < 0, _("deleted block 1?"));
3649 }
3650 else
3651 {
3652 /*
3653 * delete the text by moving the next lines forwards
3654 */
3655 text_start = dp->db_txt_start;
3656 mch_memmove((char *)dp + text_start + line_size,
3657 (char *)dp + text_start, (size_t)(line_start - text_start));
3658
3659 /*
3660 * delete the index by moving the next indexes backwards
3661 * Adjust the indexes for the text movement.
3662 */
3663 for (i = idx; i < count - 1; ++i)
3664 dp->db_index[i] = dp->db_index[i + 1] + line_size;
3665
3666 dp->db_free += line_size + INDEX_SIZE;
3667 dp->db_txt_start += line_size;
3668 --(dp->db_line_count);
3669
3670 /*
3671 * mark the block dirty and make sure it is in the file (for recovery)
3672 */
3673 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
3674 }
3675
3676#ifdef FEAT_BYTEOFF
3677 ml_updatechunk(buf, lnum, line_size, ML_CHNK_DELLINE);
3678#endif
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003679 ret = OK;
3680
3681theend:
3682#ifdef FEAT_TEXT_PROP
3683 if (textprop_save != NULL)
3684 {
3685 // Adjust text properties in the line above and below.
3686 if (lnum > 1)
3687 adjust_text_props_for_delete(buf, lnum - 1, textprop_save, textprop_save_len, TRUE);
3688 if (lnum <= buf->b_ml.ml_line_count)
3689 adjust_text_props_for_delete(buf, lnum, textprop_save, textprop_save_len, FALSE);
3690 }
3691 vim_free(textprop_save);
3692#endif
3693 return ret;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003694}
3695
3696/*
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003697 * set the DB_MARKED flag for line 'lnum'
Bram Moolenaar071d4272004-06-13 20:20:40 +00003698 */
3699 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003700ml_setmarked(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003701{
3702 bhdr_T *hp;
3703 DATA_BL *dp;
3704 /* invalid line number */
3705 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count
3706 || curbuf->b_ml.ml_mfp == NULL)
3707 return; /* give error message? */
3708
3709 if (lowest_marked == 0 || lowest_marked > lnum)
3710 lowest_marked = lnum;
3711
3712 /*
3713 * find the data block containing the line
3714 * This also fills the stack with the blocks from the root to the data block
3715 * This also releases any locked block.
3716 */
3717 if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
3718 return; /* give error message? */
3719
3720 dp = (DATA_BL *)(hp->bh_data);
3721 dp->db_index[lnum - curbuf->b_ml.ml_locked_low] |= DB_MARKED;
3722 curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
3723}
3724
3725/*
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003726 * find the first line with its DB_MARKED flag set
Bram Moolenaar071d4272004-06-13 20:20:40 +00003727 */
3728 linenr_T
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003729ml_firstmarked(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003730{
3731 bhdr_T *hp;
3732 DATA_BL *dp;
3733 linenr_T lnum;
3734 int i;
3735
3736 if (curbuf->b_ml.ml_mfp == NULL)
3737 return (linenr_T) 0;
3738
3739 /*
3740 * The search starts with lowest_marked line. This is the last line where
3741 * a mark was found, adjusted by inserting/deleting lines.
3742 */
3743 for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count; )
3744 {
3745 /*
3746 * Find the data block containing the line.
3747 * This also fills the stack with the blocks from the root to the data
3748 * block This also releases any locked block.
3749 */
3750 if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
3751 return (linenr_T)0; /* give error message? */
3752
3753 dp = (DATA_BL *)(hp->bh_data);
3754
3755 for (i = lnum - curbuf->b_ml.ml_locked_low;
3756 lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum)
3757 if ((dp->db_index[i]) & DB_MARKED)
3758 {
3759 (dp->db_index[i]) &= DB_INDEX_MASK;
3760 curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
3761 lowest_marked = lnum + 1;
3762 return lnum;
3763 }
3764 }
3765
3766 return (linenr_T) 0;
3767}
3768
Bram Moolenaar071d4272004-06-13 20:20:40 +00003769/*
3770 * clear all DB_MARKED flags
3771 */
3772 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003773ml_clearmarked(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003774{
3775 bhdr_T *hp;
3776 DATA_BL *dp;
3777 linenr_T lnum;
3778 int i;
3779
3780 if (curbuf->b_ml.ml_mfp == NULL) /* nothing to do */
3781 return;
3782
3783 /*
3784 * The search starts with line lowest_marked.
3785 */
3786 for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count; )
3787 {
3788 /*
3789 * Find the data block containing the line.
3790 * This also fills the stack with the blocks from the root to the data
3791 * block and releases any locked block.
3792 */
3793 if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
3794 return; /* give error message? */
3795
3796 dp = (DATA_BL *)(hp->bh_data);
3797
3798 for (i = lnum - curbuf->b_ml.ml_locked_low;
3799 lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum)
3800 if ((dp->db_index[i]) & DB_MARKED)
3801 {
3802 (dp->db_index[i]) &= DB_INDEX_MASK;
3803 curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
3804 }
3805 }
3806
3807 lowest_marked = 0;
3808 return;
3809}
3810
3811/*
3812 * flush ml_line if necessary
3813 */
3814 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003815ml_flush_line(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003816{
3817 bhdr_T *hp;
3818 DATA_BL *dp;
3819 linenr_T lnum;
3820 char_u *new_line;
3821 char_u *old_line;
3822 colnr_T new_len;
3823 int old_len;
3824 int extra;
3825 int idx;
3826 int start;
3827 int count;
3828 int i;
Bram Moolenaar0ca4b352010-02-11 18:54:43 +01003829 static int entered = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003830
3831 if (buf->b_ml.ml_line_lnum == 0 || buf->b_ml.ml_mfp == NULL)
3832 return; /* nothing to do */
3833
3834 if (buf->b_ml.ml_flags & ML_LINE_DIRTY)
3835 {
Bram Moolenaar0ca4b352010-02-11 18:54:43 +01003836 /* This code doesn't work recursively, but Netbeans may call back here
3837 * when obtaining the cursor position. */
3838 if (entered)
3839 return;
3840 entered = TRUE;
3841
Bram Moolenaar071d4272004-06-13 20:20:40 +00003842 lnum = buf->b_ml.ml_line_lnum;
3843 new_line = buf->b_ml.ml_line_ptr;
3844
3845 hp = ml_find_line(buf, lnum, ML_FIND);
3846 if (hp == NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003847 siemsg(_("E320: Cannot find line %ld"), lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003848 else
3849 {
3850 dp = (DATA_BL *)(hp->bh_data);
3851 idx = lnum - buf->b_ml.ml_locked_low;
3852 start = ((dp->db_index[idx]) & DB_INDEX_MASK);
3853 old_line = (char_u *)dp + start;
3854 if (idx == 0) /* line is last in block */
3855 old_len = dp->db_txt_end - start;
3856 else /* text of previous line follows */
3857 old_len = (dp->db_index[idx - 1] & DB_INDEX_MASK) - start;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01003858 new_len = buf->b_ml.ml_line_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003859 extra = new_len - old_len; /* negative if lines gets smaller */
3860
3861 /*
3862 * if new line fits in data block, replace directly
3863 */
3864 if ((int)dp->db_free >= extra)
3865 {
3866 /* if the length changes and there are following lines */
3867 count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 1;
3868 if (extra != 0 && idx < count - 1)
3869 {
3870 /* move text of following lines */
3871 mch_memmove((char *)dp + dp->db_txt_start - extra,
3872 (char *)dp + dp->db_txt_start,
3873 (size_t)(start - dp->db_txt_start));
3874
3875 /* adjust pointers of this and following lines */
3876 for (i = idx + 1; i < count; ++i)
3877 dp->db_index[i] -= extra;
3878 }
3879 dp->db_index[idx] -= extra;
3880
3881 /* adjust free space */
3882 dp->db_free -= extra;
3883 dp->db_txt_start -= extra;
3884
3885 /* copy new line into the data block */
3886 mch_memmove(old_line - extra, new_line, (size_t)new_len);
3887 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
3888#ifdef FEAT_BYTEOFF
3889 /* The else case is already covered by the insert and delete */
3890 ml_updatechunk(buf, lnum, (long)extra, ML_CHNK_UPDLINE);
3891#endif
3892 }
3893 else
3894 {
3895 /*
3896 * Cannot do it in one data block: Delete and append.
3897 * Append first, because ml_delete_int() cannot delete the
3898 * last line in a buffer, which causes trouble for a buffer
3899 * that has only one line.
3900 * Don't forget to copy the mark!
3901 */
3902 /* How about handling errors??? */
3903 (void)ml_append_int(buf, lnum, new_line, new_len, FALSE,
3904 (dp->db_index[idx] & DB_MARKED));
3905 (void)ml_delete_int(buf, lnum, FALSE);
3906 }
3907 }
3908 vim_free(new_line);
Bram Moolenaar0ca4b352010-02-11 18:54:43 +01003909
3910 entered = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003911 }
3912
3913 buf->b_ml.ml_line_lnum = 0;
3914}
3915
3916/*
3917 * create a new, empty, data block
3918 */
3919 static bhdr_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003920ml_new_data(memfile_T *mfp, int negative, int page_count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003921{
3922 bhdr_T *hp;
3923 DATA_BL *dp;
3924
3925 if ((hp = mf_new(mfp, negative, page_count)) == NULL)
3926 return NULL;
3927
3928 dp = (DATA_BL *)(hp->bh_data);
3929 dp->db_id = DATA_ID;
3930 dp->db_txt_start = dp->db_txt_end = page_count * mfp->mf_page_size;
3931 dp->db_free = dp->db_txt_start - HEADER_SIZE;
3932 dp->db_line_count = 0;
3933
3934 return hp;
3935}
3936
3937/*
3938 * create a new, empty, pointer block
3939 */
3940 static bhdr_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003941ml_new_ptr(memfile_T *mfp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003942{
3943 bhdr_T *hp;
3944 PTR_BL *pp;
3945
3946 if ((hp = mf_new(mfp, FALSE, 1)) == NULL)
3947 return NULL;
3948
3949 pp = (PTR_BL *)(hp->bh_data);
3950 pp->pb_id = PTR_ID;
3951 pp->pb_count = 0;
Bram Moolenaar20a825a2010-05-31 21:27:30 +02003952 pp->pb_count_max = (short_u)((mfp->mf_page_size - sizeof(PTR_BL))
3953 / sizeof(PTR_EN) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003954
3955 return hp;
3956}
3957
3958/*
Bram Moolenaarc1a9bc12018-12-28 21:59:29 +01003959 * Lookup line 'lnum' in a memline.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003960 *
3961 * action: if ML_DELETE or ML_INSERT the line count is updated while searching
3962 * if ML_FLUSH only flush a locked block
3963 * if ML_FIND just find the line
3964 *
3965 * If the block was found it is locked and put in ml_locked.
3966 * The stack is updated to lead to the locked block. The ip_high field in
3967 * the stack is updated to reflect the last line in the block AFTER the
3968 * insert or delete, also if the pointer block has not been updated yet. But
Bram Moolenaar6b803a72007-05-06 14:25:46 +00003969 * if ml_locked != NULL ml_locked_lineadd must be added to ip_high.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003970 *
3971 * return: NULL for failure, pointer to block header otherwise
3972 */
3973 static bhdr_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01003974ml_find_line(buf_T *buf, linenr_T lnum, int action)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003975{
3976 DATA_BL *dp;
3977 PTR_BL *pp;
3978 infoptr_T *ip;
3979 bhdr_T *hp;
3980 memfile_T *mfp;
3981 linenr_T t;
3982 blocknr_T bnum, bnum2;
3983 int dirty;
3984 linenr_T low, high;
3985 int top;
3986 int page_count;
3987 int idx;
3988
3989 mfp = buf->b_ml.ml_mfp;
3990
3991 /*
3992 * If there is a locked block check if the wanted line is in it.
3993 * If not, flush and release the locked block.
3994 * Don't do this for ML_INSERT_SAME, because the stack need to be updated.
3995 * Don't do this for ML_FLUSH, because we want to flush the locked block.
Bram Moolenaar47b8b152007-02-07 02:41:57 +00003996 * Don't do this when 'swapfile' is reset, we want to load all the blocks.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003997 */
3998 if (buf->b_ml.ml_locked)
3999 {
Bram Moolenaar47b8b152007-02-07 02:41:57 +00004000 if (ML_SIMPLE(action)
4001 && buf->b_ml.ml_locked_low <= lnum
4002 && buf->b_ml.ml_locked_high >= lnum
4003 && !mf_dont_release)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004004 {
Bram Moolenaar47b8b152007-02-07 02:41:57 +00004005 /* remember to update pointer blocks and stack later */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004006 if (action == ML_INSERT)
4007 {
4008 ++(buf->b_ml.ml_locked_lineadd);
4009 ++(buf->b_ml.ml_locked_high);
4010 }
4011 else if (action == ML_DELETE)
4012 {
4013 --(buf->b_ml.ml_locked_lineadd);
4014 --(buf->b_ml.ml_locked_high);
4015 }
4016 return (buf->b_ml.ml_locked);
4017 }
4018
4019 mf_put(mfp, buf->b_ml.ml_locked, buf->b_ml.ml_flags & ML_LOCKED_DIRTY,
4020 buf->b_ml.ml_flags & ML_LOCKED_POS);
4021 buf->b_ml.ml_locked = NULL;
4022
Bram Moolenaar6b803a72007-05-06 14:25:46 +00004023 /*
4024 * If lines have been added or deleted in the locked block, need to
4025 * update the line count in pointer blocks.
4026 */
4027 if (buf->b_ml.ml_locked_lineadd != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004028 ml_lineadd(buf, buf->b_ml.ml_locked_lineadd);
4029 }
4030
4031 if (action == ML_FLUSH) /* nothing else to do */
4032 return NULL;
4033
4034 bnum = 1; /* start at the root of the tree */
4035 page_count = 1;
4036 low = 1;
4037 high = buf->b_ml.ml_line_count;
4038
4039 if (action == ML_FIND) /* first try stack entries */
4040 {
4041 for (top = buf->b_ml.ml_stack_top - 1; top >= 0; --top)
4042 {
4043 ip = &(buf->b_ml.ml_stack[top]);
4044 if (ip->ip_low <= lnum && ip->ip_high >= lnum)
4045 {
4046 bnum = ip->ip_bnum;
4047 low = ip->ip_low;
4048 high = ip->ip_high;
4049 buf->b_ml.ml_stack_top = top; /* truncate stack at prev entry */
4050 break;
4051 }
4052 }
4053 if (top < 0)
4054 buf->b_ml.ml_stack_top = 0; /* not found, start at the root */
4055 }
4056 else /* ML_DELETE or ML_INSERT */
4057 buf->b_ml.ml_stack_top = 0; /* start at the root */
4058
4059/*
4060 * search downwards in the tree until a data block is found
4061 */
4062 for (;;)
4063 {
4064 if ((hp = mf_get(mfp, bnum, page_count)) == NULL)
4065 goto error_noblock;
4066
4067 /*
4068 * update high for insert/delete
4069 */
4070 if (action == ML_INSERT)
4071 ++high;
4072 else if (action == ML_DELETE)
4073 --high;
4074
4075 dp = (DATA_BL *)(hp->bh_data);
4076 if (dp->db_id == DATA_ID) /* data block */
4077 {
4078 buf->b_ml.ml_locked = hp;
4079 buf->b_ml.ml_locked_low = low;
4080 buf->b_ml.ml_locked_high = high;
4081 buf->b_ml.ml_locked_lineadd = 0;
4082 buf->b_ml.ml_flags &= ~(ML_LOCKED_DIRTY | ML_LOCKED_POS);
4083 return hp;
4084 }
4085
4086 pp = (PTR_BL *)(dp); /* must be pointer block */
4087 if (pp->pb_id != PTR_ID)
4088 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004089 iemsg(_("E317: pointer block id wrong"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004090 goto error_block;
4091 }
4092
4093 if ((top = ml_add_stack(buf)) < 0) /* add new entry to stack */
4094 goto error_block;
4095 ip = &(buf->b_ml.ml_stack[top]);
4096 ip->ip_bnum = bnum;
4097 ip->ip_low = low;
4098 ip->ip_high = high;
4099 ip->ip_index = -1; /* index not known yet */
4100
4101 dirty = FALSE;
4102 for (idx = 0; idx < (int)pp->pb_count; ++idx)
4103 {
4104 t = pp->pb_pointer[idx].pe_line_count;
4105 CHECK(t == 0, _("pe_line_count is zero"));
4106 if ((low += t) > lnum)
4107 {
4108 ip->ip_index = idx;
4109 bnum = pp->pb_pointer[idx].pe_bnum;
4110 page_count = pp->pb_pointer[idx].pe_page_count;
4111 high = low - 1;
4112 low -= t;
4113
4114 /*
4115 * a negative block number may have been changed
4116 */
4117 if (bnum < 0)
4118 {
4119 bnum2 = mf_trans_del(mfp, bnum);
4120 if (bnum != bnum2)
4121 {
4122 bnum = bnum2;
4123 pp->pb_pointer[idx].pe_bnum = bnum;
4124 dirty = TRUE;
4125 }
4126 }
4127
4128 break;
4129 }
4130 }
4131 if (idx >= (int)pp->pb_count) /* past the end: something wrong! */
4132 {
4133 if (lnum > buf->b_ml.ml_line_count)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004134 siemsg(_("E322: line number out of range: %ld past the end"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00004135 lnum - buf->b_ml.ml_line_count);
4136
4137 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004138 siemsg(_("E323: line count wrong in block %ld"), bnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004139 goto error_block;
4140 }
4141 if (action == ML_DELETE)
4142 {
4143 pp->pb_pointer[idx].pe_line_count--;
4144 dirty = TRUE;
4145 }
4146 else if (action == ML_INSERT)
4147 {
4148 pp->pb_pointer[idx].pe_line_count++;
4149 dirty = TRUE;
4150 }
4151 mf_put(mfp, hp, dirty, FALSE);
4152 }
4153
4154error_block:
4155 mf_put(mfp, hp, FALSE, FALSE);
4156error_noblock:
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02004157 /*
4158 * If action is ML_DELETE or ML_INSERT we have to correct the tree for
4159 * the incremented/decremented line counts, because there won't be a line
4160 * inserted/deleted after all.
4161 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004162 if (action == ML_DELETE)
4163 ml_lineadd(buf, 1);
4164 else if (action == ML_INSERT)
4165 ml_lineadd(buf, -1);
4166 buf->b_ml.ml_stack_top = 0;
4167 return NULL;
4168}
4169
4170/*
4171 * add an entry to the info pointer stack
4172 *
4173 * return -1 for failure, number of the new entry otherwise
4174 */
4175 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004176ml_add_stack(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004177{
4178 int top;
4179 infoptr_T *newstack;
4180
4181 top = buf->b_ml.ml_stack_top;
4182
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02004183 /* may have to increase the stack size */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004184 if (top == buf->b_ml.ml_stack_size)
4185 {
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02004186 CHECK(top > 0, _("Stack size increases")); /* more than 5 levels??? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004187
4188 newstack = (infoptr_T *)alloc((unsigned)sizeof(infoptr_T) *
4189 (buf->b_ml.ml_stack_size + STACK_INCR));
4190 if (newstack == NULL)
4191 return -1;
Bram Moolenaarfbd302f2015-08-08 18:23:46 +02004192 if (top > 0)
4193 mch_memmove(newstack, buf->b_ml.ml_stack,
Bram Moolenaar8c8de832008-06-24 22:58:06 +00004194 (size_t)top * sizeof(infoptr_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004195 vim_free(buf->b_ml.ml_stack);
4196 buf->b_ml.ml_stack = newstack;
4197 buf->b_ml.ml_stack_size += STACK_INCR;
4198 }
4199
4200 buf->b_ml.ml_stack_top++;
4201 return top;
4202}
4203
4204/*
4205 * Update the pointer blocks on the stack for inserted/deleted lines.
4206 * The stack itself is also updated.
4207 *
4208 * When a insert/delete line action fails, the line is not inserted/deleted,
4209 * but the pointer blocks have already been updated. That is fixed here by
4210 * walking through the stack.
4211 *
4212 * Count is the number of lines added, negative if lines have been deleted.
4213 */
4214 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004215ml_lineadd(buf_T *buf, int count)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004216{
4217 int idx;
4218 infoptr_T *ip;
4219 PTR_BL *pp;
4220 memfile_T *mfp = buf->b_ml.ml_mfp;
4221 bhdr_T *hp;
4222
4223 for (idx = buf->b_ml.ml_stack_top - 1; idx >= 0; --idx)
4224 {
4225 ip = &(buf->b_ml.ml_stack[idx]);
4226 if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
4227 break;
4228 pp = (PTR_BL *)(hp->bh_data); /* must be pointer block */
4229 if (pp->pb_id != PTR_ID)
4230 {
4231 mf_put(mfp, hp, FALSE, FALSE);
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004232 iemsg(_("E317: pointer block id wrong 2"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004233 break;
4234 }
4235 pp->pb_pointer[ip->ip_index].pe_line_count += count;
4236 ip->ip_high += count;
4237 mf_put(mfp, hp, TRUE, FALSE);
4238 }
4239}
4240
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004241#if defined(HAVE_READLINK) || defined(PROTO)
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004242/*
4243 * Resolve a symlink in the last component of a file name.
4244 * Note that f_resolve() does it for every part of the path, we don't do that
4245 * here.
4246 * If it worked returns OK and the resolved link in "buf[MAXPATHL]".
4247 * Otherwise returns FAIL.
4248 */
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004249 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004250resolve_symlink(char_u *fname, char_u *buf)
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004251{
4252 char_u tmp[MAXPATHL];
4253 int ret;
4254 int depth = 0;
4255
4256 if (fname == NULL)
4257 return FAIL;
4258
4259 /* Put the result so far in tmp[], starting with the original name. */
4260 vim_strncpy(tmp, fname, MAXPATHL - 1);
4261
4262 for (;;)
4263 {
4264 /* Limit symlink depth to 100, catch recursive loops. */
4265 if (++depth == 100)
4266 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004267 semsg(_("E773: Symlink loop for \"%s\""), fname);
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004268 return FAIL;
4269 }
4270
4271 ret = readlink((char *)tmp, (char *)buf, MAXPATHL - 1);
4272 if (ret <= 0)
4273 {
Bram Moolenaarcc984262005-12-23 22:19:46 +00004274 if (errno == EINVAL || errno == ENOENT)
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004275 {
Bram Moolenaarcc984262005-12-23 22:19:46 +00004276 /* Found non-symlink or not existing file, stop here.
Bram Moolenaar9439cdd2009-04-22 13:39:36 +00004277 * When at the first level use the unmodified name, skip the
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004278 * call to vim_FullName(). */
4279 if (depth == 1)
4280 return FAIL;
4281
4282 /* Use the resolved name in tmp[]. */
4283 break;
4284 }
4285
4286 /* There must be some error reading links, use original name. */
4287 return FAIL;
4288 }
4289 buf[ret] = NUL;
4290
4291 /*
4292 * Check whether the symlink is relative or absolute.
4293 * If it's relative, build a new path based on the directory
4294 * portion of the filename (if any) and the path the symlink
4295 * points to.
4296 */
4297 if (mch_isFullName(buf))
4298 STRCPY(tmp, buf);
4299 else
4300 {
4301 char_u *tail;
4302
4303 tail = gettail(tmp);
4304 if (STRLEN(tail) + STRLEN(buf) >= MAXPATHL)
4305 return FAIL;
4306 STRCPY(tail, buf);
4307 }
4308 }
4309
4310 /*
4311 * Try to resolve the full name of the file so that the swapfile name will
4312 * be consistent even when opening a relative symlink from different
4313 * working directories.
4314 */
4315 return vim_FullName(tmp, buf, MAXPATHL, TRUE);
4316}
4317#endif
4318
Bram Moolenaar071d4272004-06-13 20:20:40 +00004319/*
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004320 * Make swap file name out of the file name and a directory name.
4321 * Returns pointer to allocated memory or NULL.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004322 */
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004323 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004324makeswapname(
4325 char_u *fname,
4326 char_u *ffname UNUSED,
4327 buf_T *buf,
4328 char_u *dir_name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004329{
4330 char_u *r, *s;
Bram Moolenaar9dbe4752010-05-14 17:52:42 +02004331 char_u *fname_res = fname;
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004332#ifdef HAVE_READLINK
4333 char_u fname_buf[MAXPATHL];
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004334#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004335
Bram Moolenaar4f974752019-02-17 17:44:42 +01004336#if defined(UNIX) || defined(MSWIN) // Need _very_ long file names
Bram Moolenaarb113c3a2017-02-28 21:26:17 +01004337 int len = (int)STRLEN(dir_name);
Bram Moolenaarc525e3a2017-02-18 16:59:02 +01004338
4339 s = dir_name + len;
4340 if (after_pathsep(dir_name, s) && len > 1 && s[-1] == s[-2])
Bram Moolenaar071d4272004-06-13 20:20:40 +00004341 { /* Ends with '//', Use Full path */
4342 r = NULL;
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004343 if ((s = make_percent_swname(dir_name, fname)) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004344 {
4345 r = modname(s, (char_u *)".swp", FALSE);
4346 vim_free(s);
4347 }
4348 return r;
4349 }
4350#endif
4351
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004352#ifdef HAVE_READLINK
4353 /* Expand symlink in the file name, so that we put the swap file with the
4354 * actual file instead of with the symlink. */
4355 if (resolve_symlink(fname, fname_buf) == OK)
4356 fname_res = fname_buf;
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004357#endif
4358
Bram Moolenaar071d4272004-06-13 20:20:40 +00004359 r = buf_modname(
Bram Moolenaar071d4272004-06-13 20:20:40 +00004360 (buf->b_p_sn || buf->b_shortname),
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004361 fname_res,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004362 (char_u *)
Bram Moolenaare60acc12011-05-10 16:41:25 +02004363#if defined(VMS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004364 "_swp",
4365#else
4366 ".swp",
4367#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004368 /* Prepend a '.' to the swap file name for the current directory. */
Bram Moolenaar48e330a2016-02-23 14:53:34 +01004369 dir_name[0] == '.' && dir_name[1] == NUL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004370 if (r == NULL) /* out of memory */
4371 return NULL;
4372
4373 s = get_file_in_dir(r, dir_name);
4374 vim_free(r);
4375 return s;
4376}
4377
4378/*
4379 * Get file name to use for swap file or backup file.
4380 * Use the name of the edited file "fname" and an entry in the 'dir' or 'bdir'
4381 * option "dname".
4382 * - If "dname" is ".", return "fname" (swap file in dir of file).
4383 * - If "dname" starts with "./", insert "dname" in "fname" (swap file
4384 * relative to dir of file).
4385 * - Otherwise, prepend "dname" to the tail of "fname" (swap file in specific
4386 * dir).
4387 *
4388 * The return value is an allocated string and can be NULL.
4389 */
4390 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004391get_file_in_dir(
4392 char_u *fname,
4393 char_u *dname) /* don't use "dirname", it is a global for Alpha */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004394{
4395 char_u *t;
4396 char_u *tail;
4397 char_u *retval;
4398 int save_char;
4399
4400 tail = gettail(fname);
4401
4402 if (dname[0] == '.' && dname[1] == NUL)
4403 retval = vim_strsave(fname);
4404 else if (dname[0] == '.' && vim_ispathsep(dname[1]))
4405 {
4406 if (tail == fname) /* no path before file name */
4407 retval = concat_fnames(dname + 2, tail, TRUE);
4408 else
4409 {
4410 save_char = *tail;
4411 *tail = NUL;
4412 t = concat_fnames(fname, dname + 2, TRUE);
4413 *tail = save_char;
4414 if (t == NULL) /* out of memory */
4415 retval = NULL;
4416 else
4417 {
4418 retval = concat_fnames(t, tail, TRUE);
4419 vim_free(t);
4420 }
4421 }
4422 }
4423 else
4424 retval = concat_fnames(dname, tail, TRUE);
4425
Bram Moolenaar4f974752019-02-17 17:44:42 +01004426#ifdef MSWIN
Bram Moolenaar69c35002013-11-04 02:54:12 +01004427 if (retval != NULL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01004428 for (t = gettail(retval); *t != NUL; MB_PTR_ADV(t))
Bram Moolenaar69c35002013-11-04 02:54:12 +01004429 if (*t == ':')
4430 *t = '%';
4431#endif
4432
Bram Moolenaar071d4272004-06-13 20:20:40 +00004433 return retval;
4434}
4435
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004436/*
4437 * Print the ATTENTION message: info about an existing swap file.
4438 */
4439 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004440attention_message(
4441 buf_T *buf, /* buffer being edited */
4442 char_u *fname) /* swap file name */
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004443{
Bram Moolenaar8767f522016-07-01 17:17:39 +02004444 stat_T st;
Bram Moolenaar63d25552019-05-10 21:28:38 +02004445 time_t swap_mtime;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004446
4447 ++no_wait_return;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004448 (void)emsg(_("E325: ATTENTION"));
Bram Moolenaar32526b32019-01-19 17:43:09 +01004449 msg_puts(_("\nFound a swap file by the name \""));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004450 msg_home_replace(fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004451 msg_puts("\"\n");
Bram Moolenaar63d25552019-05-10 21:28:38 +02004452 swap_mtime = swapfile_info(fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004453 msg_puts(_("While opening file \""));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004454 msg_outtrans(buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004455 msg_puts("\"\n");
Bram Moolenaard6105cb2018-10-13 19:06:27 +02004456 if (mch_stat((char *)buf->b_fname, &st) == -1)
4457 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004458 msg_puts(_(" CANNOT BE FOUND"));
Bram Moolenaard6105cb2018-10-13 19:06:27 +02004459 }
4460 else
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004461 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004462 msg_puts(_(" dated: "));
Bram Moolenaar63d25552019-05-10 21:28:38 +02004463 msg_puts(get_ctime(st.st_mtime, TRUE));
4464 if (swap_mtime != 0 && st.st_mtime > swap_mtime)
Bram Moolenaar32526b32019-01-19 17:43:09 +01004465 msg_puts(_(" NEWER than swap file!\n"));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004466 }
4467 /* Some of these messages are long to allow translation to
4468 * other languages. */
Bram Moolenaar32526b32019-01-19 17:43:09 +01004469 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"));
4470 msg_puts(_("(2) An edit session for this file crashed.\n"));
4471 msg_puts(_(" If this is the case, use \":recover\" or \"vim -r "));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004472 msg_outtrans(buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004473 msg_puts(_("\"\n to recover the changes (see \":help recovery\").\n"));
4474 msg_puts(_(" If you did this already, delete the swap file \""));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004475 msg_outtrans(fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004476 msg_puts(_("\"\n to avoid this message.\n"));
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004477 cmdline_row = msg_row;
4478 --no_wait_return;
4479}
4480
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01004481#if defined(FEAT_EVAL)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004482/*
4483 * Trigger the SwapExists autocommands.
4484 * Returns a value for equivalent to do_dialog() (see below):
4485 * 0: still need to ask for a choice
4486 * 1: open read-only
4487 * 2: edit anyway
4488 * 3: recover
4489 * 4: delete it
4490 * 5: quit
4491 * 6: abort
4492 */
4493 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004494do_swapexists(buf_T *buf, char_u *fname)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004495{
4496 set_vim_var_string(VV_SWAPNAME, fname, -1);
4497 set_vim_var_string(VV_SWAPCHOICE, NULL, -1);
4498
4499 /* Trigger SwapExists autocommands with <afile> set to the file being
Bram Moolenaar12c22ce2009-04-22 13:58:46 +00004500 * edited. Disallow changing directory here. */
4501 ++allbuf_lock;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004502 apply_autocmds(EVENT_SWAPEXISTS, buf->b_fname, NULL, FALSE, NULL);
Bram Moolenaar12c22ce2009-04-22 13:58:46 +00004503 --allbuf_lock;
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004504
4505 set_vim_var_string(VV_SWAPNAME, NULL, -1);
4506
4507 switch (*get_vim_var_str(VV_SWAPCHOICE))
4508 {
4509 case 'o': return 1;
4510 case 'e': return 2;
4511 case 'r': return 3;
4512 case 'd': return 4;
4513 case 'q': return 5;
4514 case 'a': return 6;
4515 }
4516
4517 return 0;
4518}
4519#endif
4520
Bram Moolenaar071d4272004-06-13 20:20:40 +00004521/*
4522 * Find out what name to use for the swap file for buffer 'buf'.
4523 *
4524 * Several names are tried to find one that does not exist
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004525 * Returns the name in allocated memory or NULL.
Bram Moolenaarf541c362011-10-26 11:44:18 +02004526 * When out of memory "dirp" is set to NULL.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004527 *
4528 * Note: If BASENAMELEN is not correct, you will get error messages for
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004529 * not being able to open the swap or undo file
Bram Moolenaar12c22ce2009-04-22 13:58:46 +00004530 * Note: May trigger SwapExists autocmd, pointers may change!
Bram Moolenaar071d4272004-06-13 20:20:40 +00004531 */
4532 static char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01004533findswapname(
4534 buf_T *buf,
4535 char_u **dirp, /* pointer to list of directories */
4536 char_u *old_fname) /* don't give warning for this file name */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004537{
4538 char_u *fname;
4539 int n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004540 char_u *dir_name;
4541#ifdef AMIGA
4542 BPTR fh;
4543#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004544 int r;
Bram Moolenaar69c35002013-11-04 02:54:12 +01004545 char_u *buf_fname = buf->b_fname;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004546
Bram Moolenaar48e330a2016-02-23 14:53:34 +01004547#if !defined(UNIX)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004548# define CREATE_DUMMY_FILE
4549 FILE *dummyfd = NULL;
4550
Bram Moolenaar4f974752019-02-17 17:44:42 +01004551# ifdef MSWIN
Bram Moolenaar69c35002013-11-04 02:54:12 +01004552 if (buf_fname != NULL && !mch_isFullName(buf_fname)
4553 && vim_strchr(gettail(buf_fname), ':'))
4554 {
4555 char_u *t;
4556
4557 buf_fname = vim_strsave(buf_fname);
4558 if (buf_fname == NULL)
4559 buf_fname = buf->b_fname;
4560 else
Bram Moolenaar91acfff2017-03-12 19:22:36 +01004561 for (t = gettail(buf_fname); *t != NUL; MB_PTR_ADV(t))
Bram Moolenaar69c35002013-11-04 02:54:12 +01004562 if (*t == ':')
4563 *t = '%';
4564 }
4565# endif
4566
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004567 /*
4568 * If we start editing a new file, e.g. "test.doc", which resides on an
4569 * MSDOS compatible filesystem, it is possible that the file
4570 * "test.doc.swp" which we create will be exactly the same file. To avoid
4571 * this problem we temporarily create "test.doc". Don't do this when the
4572 * check below for a 8.3 file name is used.
4573 */
Bram Moolenaar69c35002013-11-04 02:54:12 +01004574 if (!(buf->b_p_sn || buf->b_shortname) && buf_fname != NULL
4575 && mch_getperm(buf_fname) < 0)
4576 dummyfd = mch_fopen((char *)buf_fname, "w");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004577#endif
4578
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004579 /*
4580 * Isolate a directory name from *dirp and put it in dir_name.
4581 * First allocate some memory to put the directory name in.
4582 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004583 dir_name = alloc((unsigned)STRLEN(*dirp) + 1);
Bram Moolenaarf541c362011-10-26 11:44:18 +02004584 if (dir_name == NULL)
4585 *dirp = NULL;
4586 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004587 (void)copy_option_part(dirp, dir_name, 31000, ",");
4588
Bram Moolenaar55debbe2010-05-23 23:34:36 +02004589 /*
4590 * we try different names until we find one that does not exist yet
4591 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004592 if (dir_name == NULL) /* out of memory */
4593 fname = NULL;
4594 else
Bram Moolenaar69c35002013-11-04 02:54:12 +01004595 fname = makeswapname(buf_fname, buf->b_ffname, buf, dir_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004596
4597 for (;;)
4598 {
4599 if (fname == NULL) /* must be out of memory */
4600 break;
4601 if ((n = (int)STRLEN(fname)) == 0) /* safety check */
4602 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01004603 VIM_CLEAR(fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004604 break;
4605 }
Bram Moolenaar48e330a2016-02-23 14:53:34 +01004606#if defined(UNIX)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004607/*
4608 * Some systems have a MS-DOS compatible filesystem that use 8.3 character
4609 * file names. If this is the first try and the swap file name does not fit in
4610 * 8.3, detect if this is the case, set shortname and try again.
4611 */
4612 if (fname[n - 2] == 'w' && fname[n - 1] == 'p'
4613 && !(buf->b_p_sn || buf->b_shortname))
4614 {
4615 char_u *tail;
4616 char_u *fname2;
Bram Moolenaar8767f522016-07-01 17:17:39 +02004617 stat_T s1, s2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004618 int f1, f2;
4619 int created1 = FALSE, created2 = FALSE;
4620 int same = FALSE;
4621
4622 /*
4623 * Check if swapfile name does not fit in 8.3:
4624 * It either contains two dots, is longer than 8 chars, or starts
4625 * with a dot.
4626 */
Bram Moolenaar69c35002013-11-04 02:54:12 +01004627 tail = gettail(buf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004628 if ( vim_strchr(tail, '.') != NULL
4629 || STRLEN(tail) > (size_t)8
4630 || *gettail(fname) == '.')
4631 {
4632 fname2 = alloc(n + 2);
4633 if (fname2 != NULL)
4634 {
4635 STRCPY(fname2, fname);
4636 /* if fname == "xx.xx.swp", fname2 = "xx.xx.swx"
4637 * if fname == ".xx.swp", fname2 = ".xx.swpx"
4638 * if fname == "123456789.swp", fname2 = "12345678x.swp"
4639 */
4640 if (vim_strchr(tail, '.') != NULL)
4641 fname2[n - 1] = 'x';
4642 else if (*gettail(fname) == '.')
4643 {
4644 fname2[n] = 'x';
4645 fname2[n + 1] = NUL;
4646 }
4647 else
4648 fname2[n - 5] += 1;
4649 /*
4650 * may need to create the files to be able to use mch_stat()
4651 */
4652 f1 = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
4653 if (f1 < 0)
4654 {
4655 f1 = mch_open_rw((char *)fname,
4656 O_RDWR|O_CREAT|O_EXCL|O_EXTRA);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004657 created1 = TRUE;
4658 }
4659 if (f1 >= 0)
4660 {
4661 f2 = mch_open((char *)fname2, O_RDONLY | O_EXTRA, 0);
4662 if (f2 < 0)
4663 {
4664 f2 = mch_open_rw((char *)fname2,
4665 O_RDWR|O_CREAT|O_EXCL|O_EXTRA);
4666 created2 = TRUE;
4667 }
4668 if (f2 >= 0)
4669 {
4670 /*
4671 * Both files exist now. If mch_stat() returns the
4672 * same device and inode they are the same file.
4673 */
4674 if (mch_fstat(f1, &s1) != -1
4675 && mch_fstat(f2, &s2) != -1
4676 && s1.st_dev == s2.st_dev
4677 && s1.st_ino == s2.st_ino)
4678 same = TRUE;
4679 close(f2);
4680 if (created2)
4681 mch_remove(fname2);
4682 }
4683 close(f1);
4684 if (created1)
4685 mch_remove(fname);
4686 }
4687 vim_free(fname2);
4688 if (same)
4689 {
4690 buf->b_shortname = TRUE;
4691 vim_free(fname);
Bram Moolenaar69c35002013-11-04 02:54:12 +01004692 fname = makeswapname(buf_fname, buf->b_ffname,
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004693 buf, dir_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004694 continue; /* try again with b_shortname set */
4695 }
4696 }
4697 }
4698 }
4699#endif
4700 /*
4701 * check if the swapfile already exists
4702 */
4703 if (mch_getperm(fname) < 0) /* it does not exist */
4704 {
4705#ifdef HAVE_LSTAT
Bram Moolenaar8767f522016-07-01 17:17:39 +02004706 stat_T sb;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004707
4708 /*
4709 * Extra security check: When a swap file is a symbolic link, this
4710 * is most likely a symlink attack.
4711 */
4712 if (mch_lstat((char *)fname, &sb) < 0)
4713#else
4714# ifdef AMIGA
4715 fh = Open((UBYTE *)fname, (long)MODE_NEWFILE);
4716 /*
4717 * on the Amiga mch_getperm() will return -1 when the file exists
4718 * but is being used by another program. This happens if you edit
4719 * a file twice.
4720 */
4721 if (fh != (BPTR)NULL) /* can open file, OK */
4722 {
4723 Close(fh);
4724 mch_remove(fname);
4725 break;
4726 }
4727 if (IoErr() != ERROR_OBJECT_IN_USE
4728 && IoErr() != ERROR_OBJECT_EXISTS)
4729# endif
4730#endif
4731 break;
4732 }
4733
4734 /*
4735 * A file name equal to old_fname is OK to use.
4736 */
4737 if (old_fname != NULL && fnamecmp(fname, old_fname) == 0)
4738 break;
4739
4740 /*
4741 * get here when file already exists
4742 */
4743 if (fname[n - 2] == 'w' && fname[n - 1] == 'p') /* first try */
4744 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00004745 /*
4746 * on MS-DOS compatible filesystems (e.g. messydos) file.doc.swp
4747 * and file.doc are the same file. To guess if this problem is
4748 * present try if file.doc.swx exists. If it does, we set
4749 * buf->b_shortname and try file_doc.swp (dots replaced by
4750 * underscores for this file), and try again. If it doesn't we
4751 * assume that "file.doc.swp" already exists.
4752 */
4753 if (!(buf->b_p_sn || buf->b_shortname)) /* not tried yet */
4754 {
4755 fname[n - 1] = 'x';
4756 r = mch_getperm(fname); /* try "file.swx" */
4757 fname[n - 1] = 'p';
4758 if (r >= 0) /* "file.swx" seems to exist */
4759 {
4760 buf->b_shortname = TRUE;
4761 vim_free(fname);
Bram Moolenaar69c35002013-11-04 02:54:12 +01004762 fname = makeswapname(buf_fname, buf->b_ffname,
Bram Moolenaar04a09c12005-08-01 22:02:32 +00004763 buf, dir_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004764 continue; /* try again with '.' replaced with '_' */
4765 }
4766 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004767 /*
4768 * If we get here the ".swp" file really exists.
4769 * Give an error message, unless recovering, no file name, we are
4770 * viewing a help file or when the path of the file is different
4771 * (happens when all .swp files are in one directory).
4772 */
Bram Moolenaar69c35002013-11-04 02:54:12 +01004773 if (!recoverymode && buf_fname != NULL
Bram Moolenaar8fc061c2004-12-29 21:03:02 +00004774 && !buf->b_help && !(buf->b_flags & BF_DUMMY))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004775 {
4776 int fd;
4777 struct block0 b0;
4778 int differ = FALSE;
4779
4780 /*
4781 * Try to read block 0 from the swap file to get the original
4782 * file name (and inode number).
4783 */
4784 fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
4785 if (fd >= 0)
4786 {
Bram Moolenaar540fc6f2010-12-17 16:27:16 +01004787 if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004788 {
4789 /*
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004790 * If the swapfile has the same directory as the
4791 * buffer don't compare the directory names, they can
4792 * have a different mountpoint.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004793 */
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004794 if (b0.b0_flags & B0_SAME_DIR)
4795 {
4796 if (fnamecmp(gettail(buf->b_ffname),
4797 gettail(b0.b0_fname)) != 0
4798 || !same_directory(fname, buf->b_ffname))
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004799 {
4800#ifdef CHECK_INODE
4801 /* Symlinks may point to the same file even
4802 * when the name differs, need to check the
4803 * inode too. */
4804 expand_env(b0.b0_fname, NameBuff, MAXPATHL);
4805 if (fnamecmp_ino(buf->b_ffname, NameBuff,
4806 char_to_long(b0.b0_ino)))
4807#endif
4808 differ = TRUE;
4809 }
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004810 }
4811 else
4812 {
4813 /*
4814 * The name in the swap file may be
4815 * "~user/path/file". Expand it first.
4816 */
4817 expand_env(b0.b0_fname, NameBuff, MAXPATHL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004818#ifdef CHECK_INODE
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004819 if (fnamecmp_ino(buf->b_ffname, NameBuff,
Bram Moolenaar900b4d72005-12-12 22:05:50 +00004820 char_to_long(b0.b0_ino)))
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004821 differ = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004822#else
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004823 if (fnamecmp(NameBuff, buf->b_ffname) != 0)
4824 differ = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004825#endif
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004826 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004827 }
4828 close(fd);
4829 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004830
4831 /* give the ATTENTION message when there is an old swap file
4832 * for the current file, and the buffer was not recovered. */
4833 if (differ == FALSE && !(curbuf->b_flags & BF_RECOVERED)
4834 && vim_strchr(p_shm, SHM_ATTENTION) == NULL)
4835 {
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004836 int choice = 0;
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02004837 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004838#ifdef CREATE_DUMMY_FILE
4839 int did_use_dummy = FALSE;
4840
4841 /* Avoid getting a warning for the file being created
4842 * outside of Vim, it was created at the start of this
4843 * function. Delete the file now, because Vim might exit
4844 * here if the window is closed. */
4845 if (dummyfd != NULL)
4846 {
4847 fclose(dummyfd);
4848 dummyfd = NULL;
Bram Moolenaar69c35002013-11-04 02:54:12 +01004849 mch_remove(buf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004850 did_use_dummy = TRUE;
4851 }
4852#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004853
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02004854#ifdef HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar071d4272004-06-13 20:20:40 +00004855 process_still_running = FALSE;
4856#endif
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02004857 // It's safe to delete the swap file if all these are true:
4858 // - the edited file exists
4859 // - the swap file has no changes and looks OK
4860 if (mch_stat((char *)buf->b_fname, &st) == 0
4861 && swapfile_unchanged(fname))
4862 {
4863 choice = 4;
4864 if (p_verbose > 0)
4865 verb_msg(_("Found a swap file that is not useful, deleting it"));
4866 }
4867
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01004868#if defined(FEAT_EVAL)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004869 /*
4870 * If there is an SwapExists autocommand and we can handle
4871 * the response, trigger it. It may return 0 to ask the
4872 * user anyway.
4873 */
Bram Moolenaar67cf86b2019-04-28 22:25:38 +02004874 if (choice == 0
4875 && swap_exists_action != SEA_NONE
Bram Moolenaar69c35002013-11-04 02:54:12 +01004876 && has_autocmd(EVENT_SWAPEXISTS, buf_fname, buf))
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004877 choice = do_swapexists(buf, fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004878
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004879 if (choice == 0)
4880#endif
4881 {
4882#ifdef FEAT_GUI
Bram Moolenaar798184c2018-10-07 20:48:39 +02004883 // If we are supposed to start the GUI but it wasn't
4884 // completely started yet, start it now. This makes
4885 // the messages displayed in the Vim window when
4886 // loading a session from the .gvimrc file.
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004887 if (gui.starting && !gui.in_use)
Bram Moolenaarafde13b2019-04-28 19:46:49 +02004888 gui_start(NULL);
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004889#endif
Bram Moolenaar798184c2018-10-07 20:48:39 +02004890 // Show info about the existing swap file.
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004891 attention_message(buf, fname);
4892
Bram Moolenaar798184c2018-10-07 20:48:39 +02004893 // We don't want a 'q' typed at the more-prompt
4894 // interrupt loading a file.
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004895 got_int = FALSE;
Bram Moolenaar798184c2018-10-07 20:48:39 +02004896
4897 // If vimrc has "simalt ~x" we don't want it to
4898 // interfere with the prompt here.
Bram Moolenaar6a2633b2018-10-07 23:16:36 +02004899 flush_buffers(FLUSH_TYPEAHEAD);
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004900 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004901
4902#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004903 if (swap_exists_action != SEA_NONE && choice == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004904 {
4905 char_u *name;
4906
4907 name = alloc((unsigned)(STRLEN(fname)
4908 + STRLEN(_("Swap file \""))
4909 + STRLEN(_("\" already exists!")) + 5));
4910 if (name != NULL)
4911 {
4912 STRCPY(name, _("Swap file \""));
4913 home_replace(NULL, fname, name + STRLEN(name),
4914 1000, TRUE);
4915 STRCAT(name, _("\" already exists!"));
4916 }
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004917 choice = do_dialog(VIM_WARNING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004918 (char_u *)_("VIM - ATTENTION"),
4919 name == NULL
4920 ? (char_u *)_("Swap file already exists!")
4921 : name,
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02004922# ifdef HAVE_PROCESS_STILL_RUNNING
Bram Moolenaar071d4272004-06-13 20:20:40 +00004923 process_still_running
4924 ? (char_u *)_("&Open Read-Only\n&Edit anyway\n&Recover\n&Quit\n&Abort") :
4925# endif
Bram Moolenaard2c340a2011-01-17 20:08:11 +01004926 (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 +00004927
Bram Moolenaar1b243ea2019-04-28 22:50:40 +02004928# ifdef HAVE_PROCESS_STILL_RUNNING
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004929 if (process_still_running && choice >= 4)
4930 choice++; /* Skip missing "Delete it" button */
4931# endif
4932 vim_free(name);
4933
4934 /* pretend screen didn't scroll, need redraw anyway */
4935 msg_scrolled = 0;
4936 redraw_all_later(NOT_VALID);
4937 }
4938#endif
4939
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004940 if (choice > 0)
4941 {
4942 switch (choice)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004943 {
4944 case 1:
4945 buf->b_p_ro = TRUE;
4946 break;
4947 case 2:
4948 break;
4949 case 3:
4950 swap_exists_action = SEA_RECOVER;
4951 break;
4952 case 4:
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004953 mch_remove(fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004954 break;
4955 case 5:
4956 swap_exists_action = SEA_QUIT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004957 break;
4958 case 6:
Bram Moolenaard5bc83f2005-12-07 21:07:59 +00004959 swap_exists_action = SEA_QUIT;
4960 got_int = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004961 break;
4962 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004963
4964 /* If the file was deleted this fname can be used. */
4965 if (mch_getperm(fname) < 0)
4966 break;
4967 }
4968 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004969 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01004970 msg_puts("\n");
Bram Moolenaar4770d092006-01-12 23:22:24 +00004971 if (msg_silent == 0)
4972 /* call wait_return() later */
4973 need_wait_return = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004974 }
4975
4976#ifdef CREATE_DUMMY_FILE
4977 /* Going to try another name, need the dummy file again. */
4978 if (did_use_dummy)
Bram Moolenaar69c35002013-11-04 02:54:12 +01004979 dummyfd = mch_fopen((char *)buf_fname, "w");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004980#endif
4981 }
4982 }
4983 }
4984
4985 /*
4986 * Change the ".swp" extension to find another file that can be used.
4987 * First decrement the last char: ".swo", ".swn", etc.
4988 * If that still isn't enough decrement the last but one char: ".svz"
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00004989 * Can happen when editing many "No Name" buffers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004990 */
4991 if (fname[n - 1] == 'a') /* ".s?a" */
4992 {
4993 if (fname[n - 2] == 'a') /* ".saa": tried enough, give up */
4994 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004995 emsg(_("E326: Too many swap files found"));
Bram Moolenaard23a8232018-02-10 18:45:26 +01004996 VIM_CLEAR(fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004997 break;
4998 }
4999 --fname[n - 2]; /* ".svz", ".suz", etc. */
5000 fname[n - 1] = 'z' + 1;
5001 }
5002 --fname[n - 1]; /* ".swo", ".swn", etc. */
5003 }
5004
5005 vim_free(dir_name);
5006#ifdef CREATE_DUMMY_FILE
5007 if (dummyfd != NULL) /* file has been created temporarily */
5008 {
5009 fclose(dummyfd);
Bram Moolenaar69c35002013-11-04 02:54:12 +01005010 mch_remove(buf_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005011 }
5012#endif
Bram Moolenaar4f974752019-02-17 17:44:42 +01005013#ifdef MSWIN
Bram Moolenaar69c35002013-11-04 02:54:12 +01005014 if (buf_fname != buf->b_fname)
5015 vim_free(buf_fname);
5016#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005017 return fname;
5018}
5019
5020 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005021b0_magic_wrong(ZERO_BL *b0p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005022{
5023 return (b0p->b0_magic_long != (long)B0_MAGIC_LONG
5024 || b0p->b0_magic_int != (int)B0_MAGIC_INT
5025 || b0p->b0_magic_short != (short)B0_MAGIC_SHORT
5026 || b0p->b0_magic_char != B0_MAGIC_CHAR);
5027}
5028
5029#ifdef CHECK_INODE
5030/*
5031 * Compare current file name with file name from swap file.
5032 * Try to use inode numbers when possible.
5033 * Return non-zero when files are different.
5034 *
5035 * When comparing file names a few things have to be taken into consideration:
5036 * - When working over a network the full path of a file depends on the host.
5037 * We check the inode number if possible. It is not 100% reliable though,
5038 * because the device number cannot be used over a network.
5039 * - When a file does not exist yet (editing a new file) there is no inode
5040 * number.
5041 * - The file name in a swap file may not be valid on the current host. The
5042 * "~user" form is used whenever possible to avoid this.
5043 *
5044 * This is getting complicated, let's make a table:
5045 *
5046 * ino_c ino_s fname_c fname_s differ =
5047 *
5048 * both files exist -> compare inode numbers:
5049 * != 0 != 0 X X ino_c != ino_s
5050 *
5051 * inode number(s) unknown, file names available -> compare file names
5052 * == 0 X OK OK fname_c != fname_s
5053 * X == 0 OK OK fname_c != fname_s
5054 *
5055 * current file doesn't exist, file for swap file exist, file name(s) not
5056 * available -> probably different
5057 * == 0 != 0 FAIL X TRUE
5058 * == 0 != 0 X FAIL TRUE
5059 *
5060 * current file exists, inode for swap unknown, file name(s) not
5061 * available -> probably different
5062 * != 0 == 0 FAIL X TRUE
5063 * != 0 == 0 X FAIL TRUE
5064 *
5065 * current file doesn't exist, inode for swap unknown, one file name not
5066 * available -> probably different
5067 * == 0 == 0 FAIL OK TRUE
5068 * == 0 == 0 OK FAIL TRUE
5069 *
5070 * current file doesn't exist, inode for swap unknown, both file names not
Bram Moolenaar8c3169c2018-05-12 17:04:12 +02005071 * available -> compare file names
5072 * == 0 == 0 FAIL FAIL fname_c != fname_s
Bram Moolenaar071d4272004-06-13 20:20:40 +00005073 *
5074 * Note that when the ino_t is 64 bits, only the last 32 will be used. This
5075 * can't be changed without making the block 0 incompatible with 32 bit
5076 * versions.
5077 */
5078
5079 static int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005080fnamecmp_ino(
5081 char_u *fname_c, /* current file name */
5082 char_u *fname_s, /* file name from swap file */
5083 long ino_block0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005084{
Bram Moolenaar8767f522016-07-01 17:17:39 +02005085 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005086 ino_t ino_c = 0; /* ino of current file */
5087 ino_t ino_s; /* ino of file from swap file */
5088 char_u buf_c[MAXPATHL]; /* full path of fname_c */
5089 char_u buf_s[MAXPATHL]; /* full path of fname_s */
5090 int retval_c; /* flag: buf_c valid */
5091 int retval_s; /* flag: buf_s valid */
5092
5093 if (mch_stat((char *)fname_c, &st) == 0)
5094 ino_c = (ino_t)st.st_ino;
5095
5096 /*
5097 * First we try to get the inode from the file name, because the inode in
5098 * the swap file may be outdated. If that fails (e.g. this path is not
5099 * valid on this machine), use the inode from block 0.
5100 */
5101 if (mch_stat((char *)fname_s, &st) == 0)
5102 ino_s = (ino_t)st.st_ino;
5103 else
5104 ino_s = (ino_t)ino_block0;
5105
5106 if (ino_c && ino_s)
5107 return (ino_c != ino_s);
5108
5109 /*
5110 * One of the inode numbers is unknown, try a forced vim_FullName() and
5111 * compare the file names.
5112 */
5113 retval_c = vim_FullName(fname_c, buf_c, MAXPATHL, TRUE);
5114 retval_s = vim_FullName(fname_s, buf_s, MAXPATHL, TRUE);
5115 if (retval_c == OK && retval_s == OK)
Bram Moolenaar8c3169c2018-05-12 17:04:12 +02005116 return STRCMP(buf_c, buf_s) != 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005117
5118 /*
5119 * Can't compare inodes or file names, guess that the files are different,
Bram Moolenaar8c3169c2018-05-12 17:04:12 +02005120 * unless both appear not to exist at all, then compare with the file name
5121 * in the swap file.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005122 */
5123 if (ino_s == 0 && ino_c == 0 && retval_c == FAIL && retval_s == FAIL)
Bram Moolenaar8c3169c2018-05-12 17:04:12 +02005124 return STRCMP(fname_c, fname_s) != 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005125 return TRUE;
5126}
5127#endif /* CHECK_INODE */
5128
5129/*
5130 * Move a long integer into a four byte character array.
5131 * Used for machine independency in block zero.
5132 */
5133 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005134long_to_char(long n, char_u *s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005135{
5136 s[0] = (char_u)(n & 0xff);
5137 n = (unsigned)n >> 8;
5138 s[1] = (char_u)(n & 0xff);
5139 n = (unsigned)n >> 8;
5140 s[2] = (char_u)(n & 0xff);
5141 n = (unsigned)n >> 8;
5142 s[3] = (char_u)(n & 0xff);
5143}
5144
5145 static long
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005146char_to_long(char_u *s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005147{
5148 long retval;
5149
5150 retval = s[3];
5151 retval <<= 8;
5152 retval |= s[2];
5153 retval <<= 8;
5154 retval |= s[1];
5155 retval <<= 8;
5156 retval |= s[0];
5157
5158 return retval;
5159}
5160
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005161/*
5162 * Set the flags in the first block of the swap file:
5163 * - file is modified or not: buf->b_changed
5164 * - 'fileformat'
5165 * - 'fileencoding'
5166 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005167 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005168ml_setflags(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005169{
5170 bhdr_T *hp;
5171 ZERO_BL *b0p;
5172
5173 if (!buf->b_ml.ml_mfp)
5174 return;
5175 for (hp = buf->b_ml.ml_mfp->mf_used_last; hp != NULL; hp = hp->bh_prev)
5176 {
5177 if (hp->bh_bnum == 0)
5178 {
5179 b0p = (ZERO_BL *)(hp->bh_data);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005180 b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0;
5181 b0p->b0_flags = (b0p->b0_flags & ~B0_FF_MASK)
5182 | (get_fileformat(buf) + 1);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005183 add_b0_fenc(b0p, buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005184 hp->bh_flags |= BH_DIRTY;
5185 mf_sync(buf->b_ml.ml_mfp, MFS_ZERO);
5186 break;
5187 }
5188 }
5189}
5190
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005191#if defined(FEAT_CRYPT) || defined(PROTO)
5192/*
5193 * If "data" points to a data block encrypt the text in it and return a copy
5194 * in allocated memory. Return NULL when out of memory.
5195 * Otherwise return "data".
5196 */
5197 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005198ml_encrypt_data(
5199 memfile_T *mfp,
5200 char_u *data,
Bram Moolenaar8767f522016-07-01 17:17:39 +02005201 off_T offset,
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005202 unsigned size)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005203{
5204 DATA_BL *dp = (DATA_BL *)data;
5205 char_u *head_end;
5206 char_u *text_start;
5207 char_u *new_data;
5208 int text_len;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005209 cryptstate_T *state;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005210
5211 if (dp->db_id != DATA_ID)
5212 return data;
5213
Bram Moolenaarbc563362015-06-09 18:35:25 +02005214 state = ml_crypt_prepare(mfp, offset, FALSE);
5215 if (state == NULL)
5216 return data;
5217
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005218 new_data = (char_u *)alloc(size);
5219 if (new_data == NULL)
5220 return NULL;
5221 head_end = (char_u *)(&dp->db_index[dp->db_line_count]);
5222 text_start = (char_u *)dp + dp->db_txt_start;
5223 text_len = size - dp->db_txt_start;
5224
5225 /* Copy the header and the text. */
5226 mch_memmove(new_data, dp, head_end - (char_u *)dp);
5227
5228 /* Encrypt the text. */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005229 crypt_encode(state, text_start, text_len, new_data + dp->db_txt_start);
5230 crypt_free_state(state);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005231
5232 /* Clear the gap. */
5233 if (head_end < text_start)
5234 vim_memset(new_data + (head_end - data), 0, text_start - head_end);
5235
5236 return new_data;
5237}
5238
5239/*
Bram Moolenaarbc563362015-06-09 18:35:25 +02005240 * Decrypt the text in "data" if it points to an encrypted data block.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005241 */
5242 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005243ml_decrypt_data(
5244 memfile_T *mfp,
5245 char_u *data,
Bram Moolenaar8767f522016-07-01 17:17:39 +02005246 off_T offset,
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005247 unsigned size)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005248{
5249 DATA_BL *dp = (DATA_BL *)data;
5250 char_u *head_end;
5251 char_u *text_start;
5252 int text_len;
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005253 cryptstate_T *state;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005254
5255 if (dp->db_id == DATA_ID)
5256 {
5257 head_end = (char_u *)(&dp->db_index[dp->db_line_count]);
5258 text_start = (char_u *)dp + dp->db_txt_start;
5259 text_len = dp->db_txt_end - dp->db_txt_start;
5260
5261 if (head_end > text_start || dp->db_txt_start > size
5262 || dp->db_txt_end > size)
5263 return; /* data was messed up */
5264
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005265 state = ml_crypt_prepare(mfp, offset, TRUE);
Bram Moolenaarbc563362015-06-09 18:35:25 +02005266 if (state != NULL)
5267 {
5268 /* Decrypt the text in place. */
5269 crypt_decode_inplace(state, text_start, text_len);
5270 crypt_free_state(state);
5271 }
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005272 }
5273}
5274
5275/*
5276 * Prepare for encryption/decryption, using the key, seed and offset.
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005277 * Return an allocated cryptstate_T *.
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005278 */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005279 static cryptstate_T *
Bram Moolenaar8767f522016-07-01 17:17:39 +02005280ml_crypt_prepare(memfile_T *mfp, off_T offset, int reading)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005281{
5282 buf_T *buf = mfp->mf_buffer;
5283 char_u salt[50];
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005284 int method_nr;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005285 char_u *key;
5286 char_u *seed;
5287
5288 if (reading && mfp->mf_old_key != NULL)
5289 {
5290 /* Reading back blocks with the previous key/method/seed. */
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005291 method_nr = mfp->mf_old_cm;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005292 key = mfp->mf_old_key;
5293 seed = mfp->mf_old_seed;
5294 }
5295 else
5296 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005297 method_nr = crypt_get_method_nr(buf);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005298 key = buf->b_p_key;
5299 seed = mfp->mf_seed;
5300 }
Bram Moolenaarbc563362015-06-09 18:35:25 +02005301 if (*key == NUL)
5302 return NULL;
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005303
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005304 if (method_nr == CRYPT_M_ZIP)
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005305 {
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005306 /* For PKzip: Append the offset to the key, so that we use a different
5307 * key for every block. */
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005308 vim_snprintf((char *)salt, sizeof(salt), "%s%ld", key, (long)offset);
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005309 return crypt_create(method_nr, salt, NULL, 0, NULL, 0);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005310 }
Bram Moolenaar8f4ac012014-08-10 13:38:34 +02005311
5312 /* Using blowfish or better: add salt and seed. We use the byte offset
5313 * of the block for the salt. */
5314 vim_snprintf((char *)salt, sizeof(salt), "%ld", (long)offset);
5315 return crypt_create(method_nr, key, salt, (int)STRLEN(salt),
5316 seed, MF_SEED_LEN);
Bram Moolenaara8ffcbb2010-06-21 06:15:46 +02005317}
5318
5319#endif
5320
5321
Bram Moolenaar071d4272004-06-13 20:20:40 +00005322#if defined(FEAT_BYTEOFF) || defined(PROTO)
5323
5324#define MLCS_MAXL 800 /* max no of lines in chunk */
5325#define MLCS_MINL 400 /* should be half of MLCS_MAXL */
5326
5327/*
Bram Moolenaar0ad014c2010-07-25 14:00:46 +02005328 * Keep information for finding byte offset of a line, updtype may be one of:
Bram Moolenaar071d4272004-06-13 20:20:40 +00005329 * ML_CHNK_ADDLINE: Add len to parent chunk, possibly splitting it
5330 * Careful: ML_CHNK_ADDLINE may cause ml_find_line() to be called.
5331 * ML_CHNK_DELLINE: Subtract len from parent chunk, possibly deleting it
5332 * ML_CHNK_UPDLINE: Add len to parent chunk, as a signed entity.
5333 */
5334 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005335ml_updatechunk(
5336 buf_T *buf,
5337 linenr_T line,
5338 long len,
5339 int updtype)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005340{
5341 static buf_T *ml_upd_lastbuf = NULL;
5342 static linenr_T ml_upd_lastline;
5343 static linenr_T ml_upd_lastcurline;
5344 static int ml_upd_lastcurix;
5345
5346 linenr_T curline = ml_upd_lastcurline;
5347 int curix = ml_upd_lastcurix;
5348 long size;
5349 chunksize_T *curchnk;
5350 int rest;
5351 bhdr_T *hp;
5352 DATA_BL *dp;
5353
5354 if (buf->b_ml.ml_usedchunks == -1 || len == 0)
5355 return;
5356 if (buf->b_ml.ml_chunksize == NULL)
5357 {
5358 buf->b_ml.ml_chunksize = (chunksize_T *)
5359 alloc((unsigned)sizeof(chunksize_T) * 100);
5360 if (buf->b_ml.ml_chunksize == NULL)
5361 {
5362 buf->b_ml.ml_usedchunks = -1;
5363 return;
5364 }
5365 buf->b_ml.ml_numchunks = 100;
5366 buf->b_ml.ml_usedchunks = 1;
5367 buf->b_ml.ml_chunksize[0].mlcs_numlines = 1;
5368 buf->b_ml.ml_chunksize[0].mlcs_totalsize = 1;
5369 }
5370
5371 if (updtype == ML_CHNK_UPDLINE && buf->b_ml.ml_line_count == 1)
5372 {
5373 /*
5374 * First line in empty buffer from ml_flush_line() -- reset
5375 */
5376 buf->b_ml.ml_usedchunks = 1;
5377 buf->b_ml.ml_chunksize[0].mlcs_numlines = 1;
Bram Moolenaar98aefe72018-12-13 22:20:09 +01005378 buf->b_ml.ml_chunksize[0].mlcs_totalsize = (long)buf->b_ml.ml_line_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005379 return;
5380 }
5381
5382 /*
5383 * Find chunk that our line belongs to, curline will be at start of the
5384 * chunk.
5385 */
5386 if (buf != ml_upd_lastbuf || line != ml_upd_lastline + 1
5387 || updtype != ML_CHNK_ADDLINE)
5388 {
5389 for (curline = 1, curix = 0;
5390 curix < buf->b_ml.ml_usedchunks - 1
5391 && line >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines;
5392 curix++)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005393 curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005394 }
Bram Moolenaara9a8e042018-10-30 22:15:55 +01005395 else if (curix < buf->b_ml.ml_usedchunks - 1
5396 && line >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005397 {
5398 /* Adjust cached curix & curline */
5399 curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
5400 curix++;
5401 }
5402 curchnk = buf->b_ml.ml_chunksize + curix;
5403
5404 if (updtype == ML_CHNK_DELLINE)
Bram Moolenaar5a6404c2006-11-01 17:12:57 +00005405 len = -len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005406 curchnk->mlcs_totalsize += len;
5407 if (updtype == ML_CHNK_ADDLINE)
5408 {
5409 curchnk->mlcs_numlines++;
5410
5411 /* May resize here so we don't have to do it in both cases below */
5412 if (buf->b_ml.ml_usedchunks + 1 >= buf->b_ml.ml_numchunks)
5413 {
Bram Moolenaar9abd5c62015-02-10 18:34:01 +01005414 chunksize_T *t_chunksize = buf->b_ml.ml_chunksize;
5415
Bram Moolenaar071d4272004-06-13 20:20:40 +00005416 buf->b_ml.ml_numchunks = buf->b_ml.ml_numchunks * 3 / 2;
5417 buf->b_ml.ml_chunksize = (chunksize_T *)
5418 vim_realloc(buf->b_ml.ml_chunksize,
5419 sizeof(chunksize_T) * buf->b_ml.ml_numchunks);
5420 if (buf->b_ml.ml_chunksize == NULL)
5421 {
5422 /* Hmmmm, Give up on offset for this buffer */
Bram Moolenaar9abd5c62015-02-10 18:34:01 +01005423 vim_free(t_chunksize);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005424 buf->b_ml.ml_usedchunks = -1;
5425 return;
5426 }
5427 }
5428
5429 if (buf->b_ml.ml_chunksize[curix].mlcs_numlines >= MLCS_MAXL)
5430 {
5431 int count; /* number of entries in block */
5432 int idx;
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005433 int end_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005434 int text_end;
5435 int linecnt;
5436
5437 mch_memmove(buf->b_ml.ml_chunksize + curix + 1,
5438 buf->b_ml.ml_chunksize + curix,
5439 (buf->b_ml.ml_usedchunks - curix) *
5440 sizeof(chunksize_T));
Bram Moolenaar9439cdd2009-04-22 13:39:36 +00005441 /* Compute length of first half of lines in the split chunk */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005442 size = 0;
5443 linecnt = 0;
5444 while (curline < buf->b_ml.ml_line_count
5445 && linecnt < MLCS_MINL)
5446 {
5447 if ((hp = ml_find_line(buf, curline, ML_FIND)) == NULL)
5448 {
5449 buf->b_ml.ml_usedchunks = -1;
5450 return;
5451 }
5452 dp = (DATA_BL *)(hp->bh_data);
5453 count = (long)(buf->b_ml.ml_locked_high) -
5454 (long)(buf->b_ml.ml_locked_low) + 1;
5455 idx = curline - buf->b_ml.ml_locked_low;
5456 curline = buf->b_ml.ml_locked_high + 1;
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005457
5458 // compute index of last line to use in this MEMLINE
Bram Moolenaar071d4272004-06-13 20:20:40 +00005459 rest = count - idx;
5460 if (linecnt + rest > MLCS_MINL)
5461 {
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005462 end_idx = idx + MLCS_MINL - linecnt - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005463 linecnt = MLCS_MINL;
5464 }
5465 else
5466 {
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005467 end_idx = count - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005468 linecnt += rest;
5469 }
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005470#ifdef FEAT_TEXT_PROP
5471 if (buf->b_has_textprop)
5472 {
5473 int i;
5474
5475 // We cannot use the text pointers to get the text length,
5476 // the text prop info would also be counted. Go over the
5477 // lines.
5478 for (i = end_idx; i < idx; ++i)
Bram Moolenaar4b7214e2019-01-03 21:55:32 +01005479 size += (int)STRLEN((char_u *)dp + (dp->db_index[i] & DB_INDEX_MASK)) + 1;
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005480 }
5481 else
5482#endif
5483 {
5484 if (idx == 0)/* first line in block, text at the end */
5485 text_end = dp->db_txt_end;
5486 else
5487 text_end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK);
5488 size += text_end - ((dp->db_index[end_idx]) & DB_INDEX_MASK);
5489 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005490 }
5491 buf->b_ml.ml_chunksize[curix].mlcs_numlines = linecnt;
5492 buf->b_ml.ml_chunksize[curix + 1].mlcs_numlines -= linecnt;
5493 buf->b_ml.ml_chunksize[curix].mlcs_totalsize = size;
5494 buf->b_ml.ml_chunksize[curix + 1].mlcs_totalsize -= size;
5495 buf->b_ml.ml_usedchunks++;
5496 ml_upd_lastbuf = NULL; /* Force recalc of curix & curline */
5497 return;
5498 }
5499 else if (buf->b_ml.ml_chunksize[curix].mlcs_numlines >= MLCS_MINL
5500 && curix == buf->b_ml.ml_usedchunks - 1
5501 && buf->b_ml.ml_line_count - line <= 1)
5502 {
5503 /*
5504 * We are in the last chunk and it is cheap to crate a new one
5505 * after this. Do it now to avoid the loop above later on
5506 */
5507 curchnk = buf->b_ml.ml_chunksize + curix + 1;
5508 buf->b_ml.ml_usedchunks++;
5509 if (line == buf->b_ml.ml_line_count)
5510 {
5511 curchnk->mlcs_numlines = 0;
5512 curchnk->mlcs_totalsize = 0;
5513 }
5514 else
5515 {
5516 /*
5517 * Line is just prior to last, move count for last
5518 * This is the common case when loading a new file
5519 */
5520 hp = ml_find_line(buf, buf->b_ml.ml_line_count, ML_FIND);
5521 if (hp == NULL)
5522 {
5523 buf->b_ml.ml_usedchunks = -1;
5524 return;
5525 }
5526 dp = (DATA_BL *)(hp->bh_data);
5527 if (dp->db_line_count == 1)
5528 rest = dp->db_txt_end - dp->db_txt_start;
5529 else
5530 rest =
5531 ((dp->db_index[dp->db_line_count - 2]) & DB_INDEX_MASK)
5532 - dp->db_txt_start;
5533 curchnk->mlcs_totalsize = rest;
5534 curchnk->mlcs_numlines = 1;
5535 curchnk[-1].mlcs_totalsize -= rest;
5536 curchnk[-1].mlcs_numlines -= 1;
5537 }
5538 }
5539 }
5540 else if (updtype == ML_CHNK_DELLINE)
5541 {
5542 curchnk->mlcs_numlines--;
5543 ml_upd_lastbuf = NULL; /* Force recalc of curix & curline */
5544 if (curix < (buf->b_ml.ml_usedchunks - 1)
5545 && (curchnk->mlcs_numlines + curchnk[1].mlcs_numlines)
5546 <= MLCS_MINL)
5547 {
5548 curix++;
5549 curchnk = buf->b_ml.ml_chunksize + curix;
5550 }
5551 else if (curix == 0 && curchnk->mlcs_numlines <= 0)
5552 {
5553 buf->b_ml.ml_usedchunks--;
5554 mch_memmove(buf->b_ml.ml_chunksize, buf->b_ml.ml_chunksize + 1,
5555 buf->b_ml.ml_usedchunks * sizeof(chunksize_T));
5556 return;
5557 }
5558 else if (curix == 0 || (curchnk->mlcs_numlines > 10
5559 && (curchnk->mlcs_numlines + curchnk[-1].mlcs_numlines)
5560 > MLCS_MINL))
5561 {
5562 return;
5563 }
5564
5565 /* Collapse chunks */
5566 curchnk[-1].mlcs_numlines += curchnk->mlcs_numlines;
5567 curchnk[-1].mlcs_totalsize += curchnk->mlcs_totalsize;
5568 buf->b_ml.ml_usedchunks--;
5569 if (curix < buf->b_ml.ml_usedchunks)
5570 {
5571 mch_memmove(buf->b_ml.ml_chunksize + curix,
5572 buf->b_ml.ml_chunksize + curix + 1,
5573 (buf->b_ml.ml_usedchunks - curix) *
5574 sizeof(chunksize_T));
5575 }
5576 return;
5577 }
5578 ml_upd_lastbuf = buf;
5579 ml_upd_lastline = line;
5580 ml_upd_lastcurline = curline;
5581 ml_upd_lastcurix = curix;
5582}
5583
5584/*
5585 * Find offset for line or line with offset.
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005586 * Find line with offset if "lnum" is 0; return remaining offset in offp
5587 * Find offset of line if "lnum" > 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00005588 * return -1 if information is not available
5589 */
5590 long
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005591ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005592{
5593 linenr_T curline;
5594 int curix;
5595 long size;
5596 bhdr_T *hp;
5597 DATA_BL *dp;
5598 int count; /* number of entries in block */
5599 int idx;
5600 int start_idx;
5601 int text_end;
5602 long offset;
5603 int len;
5604 int ffdos = (get_fileformat(buf) == EOL_DOS);
5605 int extra = 0;
5606
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005607 /* take care of cached line first */
5608 ml_flush_line(curbuf);
5609
Bram Moolenaar071d4272004-06-13 20:20:40 +00005610 if (buf->b_ml.ml_usedchunks == -1
5611 || buf->b_ml.ml_chunksize == NULL
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005612 || lnum < 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005613 return -1;
5614
5615 if (offp == NULL)
5616 offset = 0;
5617 else
5618 offset = *offp;
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005619 if (lnum == 0 && offset <= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005620 return 1; /* Not a "find offset" and offset 0 _must_ be in line 1 */
5621 /*
5622 * Find the last chunk before the one containing our line. Last chunk is
5623 * special because it will never qualify
5624 */
5625 curline = 1;
5626 curix = size = 0;
5627 while (curix < buf->b_ml.ml_usedchunks - 1
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005628 && ((lnum != 0
5629 && lnum >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005630 || (offset != 0
5631 && offset > size + buf->b_ml.ml_chunksize[curix].mlcs_totalsize
5632 + ffdos * buf->b_ml.ml_chunksize[curix].mlcs_numlines)))
5633 {
5634 curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
5635 size += buf->b_ml.ml_chunksize[curix].mlcs_totalsize;
5636 if (offset && ffdos)
5637 size += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
5638 curix++;
5639 }
5640
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005641 while ((lnum != 0 && curline < lnum) || (offset != 0 && size < offset))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005642 {
5643 if (curline > buf->b_ml.ml_line_count
5644 || (hp = ml_find_line(buf, curline, ML_FIND)) == NULL)
5645 return -1;
5646 dp = (DATA_BL *)(hp->bh_data);
5647 count = (long)(buf->b_ml.ml_locked_high) -
5648 (long)(buf->b_ml.ml_locked_low) + 1;
5649 start_idx = idx = curline - buf->b_ml.ml_locked_low;
5650 if (idx == 0)/* first line in block, text at the end */
5651 text_end = dp->db_txt_end;
5652 else
5653 text_end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK);
5654 /* Compute index of last line to use in this MEMLINE */
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005655 if (lnum != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005656 {
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005657 if (curline + (count - idx) >= lnum)
5658 idx += lnum - curline - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005659 else
5660 idx = count - 1;
5661 }
5662 else
5663 {
5664 extra = 0;
5665 while (offset >= size
5666 + text_end - (int)((dp->db_index[idx]) & DB_INDEX_MASK)
5667 + ffdos)
5668 {
5669 if (ffdos)
5670 size++;
5671 if (idx == count - 1)
5672 {
5673 extra = 1;
5674 break;
5675 }
5676 idx++;
5677 }
5678 }
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005679#ifdef FEAT_TEXT_PROP
5680 if (buf->b_has_textprop)
5681 {
5682 int i;
5683
5684 // cannot use the db_index pointer, need to get the actual text
5685 // lengths.
5686 len = 0;
5687 for (i = start_idx; i <= idx; ++i)
Bram Moolenaar4b7214e2019-01-03 21:55:32 +01005688 len += (int)STRLEN((char_u *)dp + ((dp->db_index[i]) & DB_INDEX_MASK)) + 1;
Bram Moolenaarb413d2e2018-12-25 23:15:46 +01005689 }
5690 else
5691#endif
5692 len = text_end - ((dp->db_index[idx]) & DB_INDEX_MASK);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005693 size += len;
5694 if (offset != 0 && size >= offset)
5695 {
5696 if (size + ffdos == offset)
5697 *offp = 0;
5698 else if (idx == start_idx)
5699 *offp = offset - size + len;
5700 else
5701 *offp = offset - size + len
5702 - (text_end - ((dp->db_index[idx - 1]) & DB_INDEX_MASK));
5703 curline += idx - start_idx + extra;
5704 if (curline > buf->b_ml.ml_line_count)
5705 return -1; /* exactly one byte beyond the end */
5706 return curline;
5707 }
5708 curline = buf->b_ml.ml_locked_high + 1;
5709 }
5710
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005711 if (lnum != 0)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005712 {
5713 /* Count extra CR characters. */
5714 if (ffdos)
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00005715 size += lnum - 1;
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005716
Bram Moolenaar34d72d42015-07-17 14:18:08 +02005717 /* Don't count the last line break if 'noeol' and ('bin' or
5718 * 'nofixeol'). */
5719 if ((!buf->b_p_fixeol || buf->b_p_bin) && !buf->b_p_eol
Bram Moolenaarc26f7c62018-08-20 22:53:04 +02005720 && lnum > buf->b_ml.ml_line_count)
Bram Moolenaar5eb86f92004-07-26 12:53:41 +00005721 size -= ffdos + 1;
5722 }
5723
Bram Moolenaar071d4272004-06-13 20:20:40 +00005724 return size;
5725}
5726
5727/*
5728 * Goto byte in buffer with offset 'cnt'.
5729 */
5730 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01005731goto_byte(long cnt)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005732{
5733 long boff = cnt;
5734 linenr_T lnum;
5735
5736 ml_flush_line(curbuf); /* cached line may be dirty */
5737 setpcmark();
5738 if (boff)
5739 --boff;
5740 lnum = ml_find_line_or_offset(curbuf, (linenr_T)0, &boff);
5741 if (lnum < 1) /* past the end */
5742 {
5743 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
5744 curwin->w_curswant = MAXCOL;
5745 coladvance((colnr_T)MAXCOL);
5746 }
5747 else
5748 {
5749 curwin->w_cursor.lnum = lnum;
5750 curwin->w_cursor.col = (colnr_T)boff;
Bram Moolenaar943d2b52005-12-02 00:50:49 +00005751 curwin->w_cursor.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005752 curwin->w_set_curswant = TRUE;
5753 }
5754 check_cursor();
5755
Bram Moolenaar071d4272004-06-13 20:20:40 +00005756 /* Make sure the cursor is on the first byte of a multi-byte char. */
5757 if (has_mbyte)
5758 mb_adjust_cursor();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005759}
5760#endif