Bram Moolenaar | edf3f97 | 2016-08-29 22:49:24 +0200 | [diff] [blame] | 1 | /* vi:set ts=8 sts=4 sw=4 noet: |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2 | * |
| 3 | * VIM - Vi IMproved by Bram Moolenaar |
| 4 | * |
| 5 | * Do ":help uganda" in Vim to read copying and usage conditions. |
| 6 | * Do ":help credits" in Vim to see a list of people who contributed. |
| 7 | * See README.txt for an overview of the Vim source code. |
| 8 | */ |
| 9 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 10 | // for debugging |
| 11 | // #define CHECK(c, s) do { if (c) emsg((s)); } while (0) |
Bram Moolenaar | 6f47002 | 2018-04-10 18:47:20 +0200 | [diff] [blame] | 12 | #define CHECK(c, s) do { /**/ } while (0) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 13 | |
| 14 | /* |
| 15 | * memline.c: Contains the functions for appending, deleting and changing the |
Bram Moolenaar | 4770d09 | 2006-01-12 23:22:24 +0000 | [diff] [blame] | 16 | * 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 Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 21 | * |
| 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 Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 45 | #include "vim.h" |
| 46 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 47 | #ifndef UNIX // it's in os_unix.h for Unix |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 48 | # include <time.h> |
| 49 | #endif |
| 50 | |
Bram Moolenaar | 5a6404c | 2006-11-01 17:12:57 +0000 | [diff] [blame] | 51 | #if defined(SASC) || defined(__amigaos4__) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 52 | # include <proto/dos.h> // for Open() and Close() |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 53 | #endif |
| 54 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 55 | typedef struct block0 ZERO_BL; // contents of the first block |
| 56 | typedef struct pointer_block PTR_BL; // contents of a pointer block |
| 57 | typedef struct data_block DATA_BL; // contents of a data block |
| 58 | typedef struct pointer_entry PTR_EN; // block/line-count pair |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 59 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 60 | #define DATA_ID (('d' << 8) + 'a') // data block id |
| 61 | #define PTR_ID (('p' << 8) + 't') // pointer block id |
| 62 | #define BLOCK0_ID0 'b' // block 0 id 0 |
| 63 | #define BLOCK0_ID1 '0' // block 0 id 1 |
| 64 | #define BLOCK0_ID1_C0 'c' // block 0 id 1 'cm' 0 |
| 65 | #define BLOCK0_ID1_C1 'C' // block 0 id 1 'cm' 1 |
| 66 | #define BLOCK0_ID1_C2 'd' // block 0 id 1 'cm' 2 |
Bram Moolenaar | 3a2a60c | 2023-05-27 18:02:55 +0100 | [diff] [blame] | 67 | // BLOCK0_ID1_C3 and BLOCK0_ID1_C4 are for libsodium encryption. However, for |
Bram Moolenaar | e1b4822 | 2023-04-24 18:11:35 +0100 | [diff] [blame] | 68 | // these the swapfile is disabled, thus they will not be used. Added for |
| 69 | // consistency anyway. |
| 70 | #define BLOCK0_ID1_C3 'S' // block 0 id 1 'cm' 3 |
| 71 | #define BLOCK0_ID1_C4 's' // block 0 id 1 'cm' 4 |
Bram Moolenaar | 8f4ac01 | 2014-08-10 13:38:34 +0200 | [diff] [blame] | 72 | |
| 73 | #if defined(FEAT_CRYPT) |
| 74 | static int id1_codes[] = { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 75 | BLOCK0_ID1_C0, // CRYPT_M_ZIP |
| 76 | BLOCK0_ID1_C1, // CRYPT_M_BF |
| 77 | BLOCK0_ID1_C2, // CRYPT_M_BF2 |
Christian Brabandt | f573c6e | 2021-06-20 14:02:16 +0200 | [diff] [blame] | 78 | BLOCK0_ID1_C3, // CRYPT_M_SOD - Unused! |
Bram Moolenaar | e1b4822 | 2023-04-24 18:11:35 +0100 | [diff] [blame] | 79 | BLOCK0_ID1_C4, // CRYPT_M_SOD2 - Unused! |
Bram Moolenaar | 8f4ac01 | 2014-08-10 13:38:34 +0200 | [diff] [blame] | 80 | }; |
| 81 | #endif |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 82 | |
| 83 | /* |
| 84 | * pointer to a block, used in a pointer block |
| 85 | */ |
| 86 | struct pointer_entry |
| 87 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 88 | blocknr_T pe_bnum; // block number |
| 89 | linenr_T pe_line_count; // number of lines in this branch |
| 90 | linenr_T pe_old_lnum; // lnum for this block (for recovery) |
| 91 | int pe_page_count; // number of pages in block pe_bnum |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 92 | }; |
| 93 | |
| 94 | /* |
| 95 | * A pointer block contains a list of branches in the tree. |
| 96 | */ |
| 97 | struct pointer_block |
| 98 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 99 | short_u pb_id; // ID for pointer block: PTR_ID |
| 100 | short_u pb_count; // number of pointers in this block |
| 101 | short_u pb_count_max; // maximum value for pb_count |
| 102 | PTR_EN pb_pointer[1]; // list of pointers to blocks (actually longer) |
| 103 | // followed by empty space until end of page |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 104 | }; |
| 105 | |
Bram Moolenaar | b67ba03 | 2023-04-22 21:14:26 +0100 | [diff] [blame] | 106 | // Value for pb_count_max. |
| 107 | #define PB_COUNT_MAX(mfp) (short_u)(((mfp)->mf_page_size - offsetof(PTR_BL, pb_pointer)) / sizeof(PTR_EN)) |
| 108 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 109 | /* |
| 110 | * A data block is a leaf in the tree. |
| 111 | * |
| 112 | * The text of the lines is at the end of the block. The text of the first line |
| 113 | * in the block is put at the end, the text of the second line in front of it, |
| 114 | * etc. Thus the order of the lines is the opposite of the line number. |
| 115 | */ |
| 116 | struct data_block |
| 117 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 118 | short_u db_id; // ID for data block: DATA_ID |
| 119 | unsigned db_free; // free space available |
| 120 | unsigned db_txt_start; // byte where text starts |
| 121 | unsigned db_txt_end; // byte just after data block |
| 122 | linenr_T db_line_count; // number of lines in this block |
| 123 | unsigned db_index[1]; // index for start of line (actually bigger) |
Bram Moolenaar | 4b96df5 | 2020-01-26 22:00:26 +0100 | [diff] [blame] | 124 | // followed by empty space up to db_txt_start |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 125 | // followed by the text in the lines until |
| 126 | // end of page |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 127 | }; |
| 128 | |
| 129 | /* |
| 130 | * The low bits of db_index hold the actual index. The topmost bit is |
| 131 | * used for the global command to be able to mark a line. |
| 132 | * This method is not clean, but otherwise there would be at least one extra |
| 133 | * byte used for each line. |
| 134 | * The mark has to be in this place to keep it with the correct line when other |
| 135 | * lines are inserted or deleted. |
| 136 | */ |
| 137 | #define DB_MARKED ((unsigned)1 << ((sizeof(unsigned) * 8) - 1)) |
| 138 | #define DB_INDEX_MASK (~DB_MARKED) |
| 139 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 140 | #define INDEX_SIZE (sizeof(unsigned)) // size of one db_index entry |
zeertzjq | 1b438a8 | 2023-02-01 13:11:15 +0000 | [diff] [blame] | 141 | #define HEADER_SIZE (offsetof(DATA_BL, db_index)) // size of data block header |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 142 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 143 | #define B0_FNAME_SIZE_ORG 900 // what it was in older versions |
| 144 | #define B0_FNAME_SIZE_NOCRYPT 898 // 2 bytes used for other things |
| 145 | #define B0_FNAME_SIZE_CRYPT 890 // 10 bytes used for other things |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 146 | #define B0_UNAME_SIZE 40 |
| 147 | #define B0_HNAME_SIZE 40 |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 148 | /* |
| 149 | * Restrict the numbers to 32 bits, otherwise most compilers will complain. |
| 150 | * This won't detect a 64 bit machine that only swaps a byte in the top 32 |
| 151 | * bits, but that is crazy anyway. |
| 152 | */ |
| 153 | #define B0_MAGIC_LONG 0x30313233L |
| 154 | #define B0_MAGIC_INT 0x20212223L |
| 155 | #define B0_MAGIC_SHORT 0x10111213L |
| 156 | #define B0_MAGIC_CHAR 0x55 |
| 157 | |
| 158 | /* |
| 159 | * Block zero holds all info about the swap file. |
| 160 | * |
| 161 | * NOTE: DEFINITION OF BLOCK 0 SHOULD NOT CHANGE! It would make all existing |
| 162 | * swap files unusable! |
| 163 | * |
| 164 | * If size of block0 changes anyway, adjust MIN_SWAP_PAGE_SIZE in vim.h!! |
| 165 | * |
Bram Moolenaar | bae0c16 | 2007-05-10 19:30:25 +0000 | [diff] [blame] | 166 | * This block is built up of single bytes, to make it portable across |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 167 | * different machines. b0_magic_* is used to check the byte order and size of |
| 168 | * variables, because the rest of the swap file is not portable. |
| 169 | */ |
| 170 | struct block0 |
| 171 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 172 | char_u b0_id[2]; // id for block 0: BLOCK0_ID0 and BLOCK0_ID1, |
| 173 | // BLOCK0_ID1_C0, BLOCK0_ID1_C1, etc. |
| 174 | char_u b0_version[10]; // Vim version string |
| 175 | char_u b0_page_size[4];// number of bytes per page |
| 176 | char_u b0_mtime[4]; // last modification time of file |
| 177 | char_u b0_ino[4]; // inode of b0_fname |
| 178 | char_u b0_pid[4]; // process id of creator (or 0) |
| 179 | char_u b0_uname[B0_UNAME_SIZE]; // name of user (uid if no name) |
| 180 | char_u b0_hname[B0_HNAME_SIZE]; // host name (if it has a name) |
| 181 | char_u b0_fname[B0_FNAME_SIZE_ORG]; // name of file being edited |
| 182 | long b0_magic_long; // check for byte order of long |
| 183 | int b0_magic_int; // check for byte order of int |
| 184 | short b0_magic_short; // check for byte order of short |
| 185 | char_u b0_magic_char; // check for last char |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 186 | }; |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 187 | |
| 188 | /* |
Bram Moolenaar | 4770d09 | 2006-01-12 23:22:24 +0000 | [diff] [blame] | 189 | * Note: b0_dirty and b0_flags are put at the end of the file name. For very |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 190 | * long file names in older versions of Vim they are invalid. |
| 191 | * The 'fileencoding' comes before b0_flags, with a NUL in front. But only |
| 192 | * when there is room, for very long file names it's omitted. |
| 193 | */ |
| 194 | #define B0_DIRTY 0x55 |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 195 | #define b0_dirty b0_fname[B0_FNAME_SIZE_ORG - 1] |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 196 | |
| 197 | /* |
| 198 | * The b0_flags field is new in Vim 7.0. |
| 199 | */ |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 200 | #define b0_flags b0_fname[B0_FNAME_SIZE_ORG - 2] |
| 201 | |
| 202 | /* |
| 203 | * Crypt seed goes here, 8 bytes. New in Vim 7.3. |
| 204 | * Without encryption these bytes may be used for 'fenc'. |
| 205 | */ |
| 206 | #define b0_seed b0_fname[B0_FNAME_SIZE_ORG - 2 - MF_SEED_LEN] |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 207 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 208 | // The lowest two bits contain the fileformat. Zero means it's not set |
| 209 | // (compatible with Vim 6.x), otherwise it's EOL_UNIX + 1, EOL_DOS + 1 or |
| 210 | // EOL_MAC + 1. |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 211 | #define B0_FF_MASK 3 |
| 212 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 213 | // Swap file is in directory of edited file. Used to find the file from |
| 214 | // different mount points. |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 215 | #define B0_SAME_DIR 4 |
| 216 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 217 | // The 'fileencoding' is at the end of b0_fname[], with a NUL in front of it. |
| 218 | // When empty there is only the NUL. |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 219 | #define B0_HAS_FENC 8 |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 220 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 221 | #define STACK_INCR 5 // nr of entries added to ml_stack at a time |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 222 | |
| 223 | /* |
| 224 | * The line number where the first mark may be is remembered. |
| 225 | * If it is 0 there are no marks at all. |
| 226 | * (always used for the current buffer only, no buffer change possible while |
| 227 | * executing a global command). |
| 228 | */ |
| 229 | static linenr_T lowest_marked = 0; |
| 230 | |
| 231 | /* |
| 232 | * arguments for ml_find_line() |
| 233 | */ |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 234 | #define ML_DELETE 0x11 // delete line |
| 235 | #define ML_INSERT 0x12 // insert line |
| 236 | #define ML_FIND 0x13 // just find the line |
| 237 | #define ML_FLUSH 0x02 // flush locked block |
kylo252 | ae6f1d8 | 2022-02-16 19:24:07 +0000 | [diff] [blame] | 238 | #define ML_SIMPLE(x) ((x) & 0x10) // DEL, INS or FIND |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 239 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 240 | // argument for ml_upd_block0() |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 241 | typedef enum { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 242 | UB_FNAME = 0 // update timestamp and filename |
| 243 | , UB_SAME_DIR // update the B0_SAME_DIR flag |
| 244 | , UB_CRYPT // update crypt key |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 245 | } upd_block0_T; |
| 246 | |
| 247 | #ifdef FEAT_CRYPT |
Bram Moolenaar | 92b8b2d | 2016-01-29 22:36:45 +0100 | [diff] [blame] | 248 | static void ml_set_b0_crypt(buf_T *buf, ZERO_BL *b0p); |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 249 | #endif |
Bram Moolenaar | 92b8b2d | 2016-01-29 22:36:45 +0100 | [diff] [blame] | 250 | static void ml_upd_block0(buf_T *buf, upd_block0_T what); |
| 251 | static void set_b0_fname(ZERO_BL *, buf_T *buf); |
| 252 | static void set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf); |
Bram Moolenaar | 92b8b2d | 2016-01-29 22:36:45 +0100 | [diff] [blame] | 253 | static void add_b0_fenc(ZERO_BL *b0p, buf_T *buf); |
Bram Moolenaar | 92b8b2d | 2016-01-29 22:36:45 +0100 | [diff] [blame] | 254 | static time_t swapfile_info(char_u *); |
| 255 | static int recov_file_names(char_u **, char_u *, int prepend_dot); |
Bram Moolenaar | 92b8b2d | 2016-01-29 22:36:45 +0100 | [diff] [blame] | 256 | static char_u *findswapname(buf_T *, char_u **, char_u *); |
| 257 | static void ml_flush_line(buf_T *); |
| 258 | static bhdr_T *ml_new_data(memfile_T *, int, int); |
| 259 | static bhdr_T *ml_new_ptr(memfile_T *); |
| 260 | static bhdr_T *ml_find_line(buf_T *, linenr_T, int); |
| 261 | static int ml_add_stack(buf_T *); |
| 262 | static void ml_lineadd(buf_T *, int); |
| 263 | static int b0_magic_wrong(ZERO_BL *); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 264 | #ifdef CHECK_INODE |
Bram Moolenaar | 92b8b2d | 2016-01-29 22:36:45 +0100 | [diff] [blame] | 265 | static int fnamecmp_ino(char_u *, char_u *, long); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 266 | #endif |
Bram Moolenaar | 92b8b2d | 2016-01-29 22:36:45 +0100 | [diff] [blame] | 267 | static void long_to_char(long, char_u *); |
| 268 | static long char_to_long(char_u *); |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 269 | #ifdef FEAT_CRYPT |
Bram Moolenaar | 8767f52 | 2016-07-01 17:17:39 +0200 | [diff] [blame] | 270 | static cryptstate_T *ml_crypt_prepare(memfile_T *mfp, off_T offset, int reading); |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 271 | #endif |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 272 | #ifdef FEAT_BYTEOFF |
Bram Moolenaar | 92b8b2d | 2016-01-29 22:36:45 +0100 | [diff] [blame] | 273 | static void ml_updatechunk(buf_T *buf, long line, long len, int updtype); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 274 | #endif |
| 275 | |
| 276 | /* |
Bram Moolenaar | 4770d09 | 2006-01-12 23:22:24 +0000 | [diff] [blame] | 277 | * Open a new memline for "buf". |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 278 | * |
Bram Moolenaar | 4770d09 | 2006-01-12 23:22:24 +0000 | [diff] [blame] | 279 | * Return FAIL for failure, OK otherwise. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 280 | */ |
| 281 | int |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 282 | ml_open(buf_T *buf) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 283 | { |
| 284 | memfile_T *mfp; |
| 285 | bhdr_T *hp = NULL; |
| 286 | ZERO_BL *b0p; |
| 287 | PTR_BL *pp; |
| 288 | DATA_BL *dp; |
| 289 | |
Bram Moolenaar | 4770d09 | 2006-01-12 23:22:24 +0000 | [diff] [blame] | 290 | /* |
| 291 | * init fields in memline struct |
| 292 | */ |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 293 | buf->b_ml.ml_stack_size = 0; // no stack yet |
| 294 | buf->b_ml.ml_stack = NULL; // no stack yet |
| 295 | buf->b_ml.ml_stack_top = 0; // nothing in the stack |
| 296 | buf->b_ml.ml_locked = NULL; // no cached block |
| 297 | buf->b_ml.ml_line_lnum = 0; // no cached line |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 298 | #ifdef FEAT_BYTEOFF |
Bram Moolenaar | 4770d09 | 2006-01-12 23:22:24 +0000 | [diff] [blame] | 299 | buf->b_ml.ml_chunksize = NULL; |
Bram Moolenaar | df9070e | 2021-08-25 17:31:37 +0200 | [diff] [blame] | 300 | buf->b_ml.ml_usedchunks = 0; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 301 | #endif |
| 302 | |
Bram Moolenaar | e100440 | 2020-10-24 20:49:43 +0200 | [diff] [blame] | 303 | if (cmdmod.cmod_flags & CMOD_NOSWAPFILE) |
Bram Moolenaar | 5803ae6 | 2014-03-23 16:04:02 +0100 | [diff] [blame] | 304 | buf->b_p_swf = FALSE; |
| 305 | |
Bram Moolenaar | 4770d09 | 2006-01-12 23:22:24 +0000 | [diff] [blame] | 306 | /* |
| 307 | * When 'updatecount' is non-zero swap file may be opened later. |
| 308 | */ |
| 309 | if (p_uc && buf->b_p_swf) |
| 310 | buf->b_may_swap = TRUE; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 311 | else |
Bram Moolenaar | 4770d09 | 2006-01-12 23:22:24 +0000 | [diff] [blame] | 312 | buf->b_may_swap = FALSE; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 313 | |
Bram Moolenaar | 4770d09 | 2006-01-12 23:22:24 +0000 | [diff] [blame] | 314 | /* |
| 315 | * Open the memfile. No swap file is created yet. |
| 316 | */ |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 317 | mfp = mf_open(NULL, 0); |
| 318 | if (mfp == NULL) |
| 319 | goto error; |
| 320 | |
Bram Moolenaar | 4770d09 | 2006-01-12 23:22:24 +0000 | [diff] [blame] | 321 | buf->b_ml.ml_mfp = mfp; |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 322 | #ifdef FEAT_CRYPT |
| 323 | mfp->mf_buffer = buf; |
| 324 | #endif |
Bram Moolenaar | 4770d09 | 2006-01-12 23:22:24 +0000 | [diff] [blame] | 325 | buf->b_ml.ml_flags = ML_EMPTY; |
| 326 | buf->b_ml.ml_line_count = 1; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 327 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 328 | /* |
| 329 | * fill block0 struct and write page 0 |
| 330 | */ |
| 331 | if ((hp = mf_new(mfp, FALSE, 1)) == NULL) |
| 332 | goto error; |
| 333 | if (hp->bh_bnum != 0) |
| 334 | { |
RestorerZ | 68ebcee | 2023-05-31 17:12:14 +0100 | [diff] [blame] | 335 | iemsg(e_didnt_get_block_nr_zero); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 336 | goto error; |
| 337 | } |
| 338 | b0p = (ZERO_BL *)(hp->bh_data); |
| 339 | |
| 340 | b0p->b0_id[0] = BLOCK0_ID0; |
| 341 | b0p->b0_id[1] = BLOCK0_ID1; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 342 | b0p->b0_magic_long = (long)B0_MAGIC_LONG; |
| 343 | b0p->b0_magic_int = (int)B0_MAGIC_INT; |
| 344 | b0p->b0_magic_short = (short)B0_MAGIC_SHORT; |
| 345 | b0p->b0_magic_char = B0_MAGIC_CHAR; |
Bram Moolenaar | 22c1056 | 2018-05-26 17:35:27 +0200 | [diff] [blame] | 346 | mch_memmove(b0p->b0_version, "VIM ", 4); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 347 | STRNCPY(b0p->b0_version + 4, Version, 6); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 348 | long_to_char((long)mfp->mf_page_size, b0p->b0_page_size); |
Bram Moolenaar | 4770d09 | 2006-01-12 23:22:24 +0000 | [diff] [blame] | 349 | |
Bram Moolenaar | 76b92b2 | 2006-03-24 22:46:53 +0000 | [diff] [blame] | 350 | #ifdef FEAT_SPELL |
| 351 | if (!buf->b_spell) |
| 352 | #endif |
Bram Moolenaar | 4770d09 | 2006-01-12 23:22:24 +0000 | [diff] [blame] | 353 | { |
| 354 | b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0; |
| 355 | b0p->b0_flags = get_fileformat(buf) + 1; |
| 356 | set_b0_fname(b0p, buf); |
| 357 | (void)get_user_name(b0p->b0_uname, B0_UNAME_SIZE); |
| 358 | b0p->b0_uname[B0_UNAME_SIZE - 1] = NUL; |
| 359 | mch_get_host_name(b0p->b0_hname, B0_HNAME_SIZE); |
| 360 | b0p->b0_hname[B0_HNAME_SIZE - 1] = NUL; |
| 361 | long_to_char(mch_get_pid(), b0p->b0_pid); |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 362 | #ifdef FEAT_CRYPT |
Bram Moolenaar | 8f4ac01 | 2014-08-10 13:38:34 +0200 | [diff] [blame] | 363 | ml_set_b0_crypt(buf, b0p); |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 364 | #endif |
Bram Moolenaar | 4770d09 | 2006-01-12 23:22:24 +0000 | [diff] [blame] | 365 | } |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 366 | |
| 367 | /* |
| 368 | * Always sync block number 0 to disk, so we can check the file name in |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 369 | * the swap file in findswapname(). Don't do this for a help files or |
| 370 | * a spell buffer though. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 371 | * Only works when there's a swapfile, otherwise it's done when the file |
| 372 | * is created. |
| 373 | */ |
| 374 | mf_put(mfp, hp, TRUE, FALSE); |
Bram Moolenaar | 4770d09 | 2006-01-12 23:22:24 +0000 | [diff] [blame] | 375 | if (!buf->b_help && !B_SPELL(buf)) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 376 | (void)mf_sync(mfp, 0); |
| 377 | |
Bram Moolenaar | 4770d09 | 2006-01-12 23:22:24 +0000 | [diff] [blame] | 378 | /* |
| 379 | * Fill in root pointer block and write page 1. |
| 380 | */ |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 381 | if ((hp = ml_new_ptr(mfp)) == NULL) |
| 382 | goto error; |
| 383 | if (hp->bh_bnum != 1) |
| 384 | { |
RestorerZ | 68ebcee | 2023-05-31 17:12:14 +0100 | [diff] [blame] | 385 | iemsg(e_didnt_get_block_nr_one); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 386 | goto error; |
| 387 | } |
| 388 | pp = (PTR_BL *)(hp->bh_data); |
| 389 | pp->pb_count = 1; |
| 390 | pp->pb_pointer[0].pe_bnum = 2; |
| 391 | pp->pb_pointer[0].pe_page_count = 1; |
| 392 | pp->pb_pointer[0].pe_old_lnum = 1; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 393 | pp->pb_pointer[0].pe_line_count = 1; // line count after insertion |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 394 | mf_put(mfp, hp, TRUE, FALSE); |
| 395 | |
Bram Moolenaar | 4770d09 | 2006-01-12 23:22:24 +0000 | [diff] [blame] | 396 | /* |
| 397 | * Allocate first data block and create an empty line 1. |
| 398 | */ |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 399 | if ((hp = ml_new_data(mfp, FALSE, 1)) == NULL) |
| 400 | goto error; |
| 401 | if (hp->bh_bnum != 2) |
| 402 | { |
RestorerZ | 68ebcee | 2023-05-31 17:12:14 +0100 | [diff] [blame] | 403 | iemsg(e_didnt_get_block_nr_two); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 404 | goto error; |
| 405 | } |
| 406 | |
| 407 | dp = (DATA_BL *)(hp->bh_data); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 408 | dp->db_index[0] = --dp->db_txt_start; // at end of block |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 409 | dp->db_free -= 1 + INDEX_SIZE; |
| 410 | dp->db_line_count = 1; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 411 | *((char_u *)dp + dp->db_txt_start) = NUL; // empty line |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 412 | |
| 413 | return OK; |
| 414 | |
| 415 | error: |
| 416 | if (mfp != NULL) |
| 417 | { |
| 418 | if (hp) |
| 419 | mf_put(mfp, hp, FALSE, FALSE); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 420 | mf_close(mfp, TRUE); // will also free(mfp->mf_fname) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 421 | } |
Bram Moolenaar | 4770d09 | 2006-01-12 23:22:24 +0000 | [diff] [blame] | 422 | buf->b_ml.ml_mfp = NULL; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 423 | return FAIL; |
| 424 | } |
| 425 | |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 426 | #if defined(FEAT_CRYPT) || defined(PROTO) |
| 427 | /* |
Christian Brabandt | 19e6c4f | 2023-06-27 18:57:10 +0100 | [diff] [blame] | 428 | * Swapfile encryption is not supported by XChaCha20. If this crypt method is |
| 429 | * used then disable the swapfile, to avoid plain text being written to disk, |
| 430 | * and return TRUE. |
| 431 | * Otherwise return FALSE. |
| 432 | */ |
| 433 | static int |
| 434 | crypt_may_close_swapfile(buf_T *buf, char_u *key, int method) |
| 435 | { |
| 436 | if (crypt_method_is_sodium(method) && *key != NUL) |
| 437 | { |
| 438 | mf_close_file(buf, TRUE); |
| 439 | buf->b_p_swf = FALSE; |
| 440 | return TRUE; |
| 441 | } |
| 442 | return FALSE; |
| 443 | } |
| 444 | |
| 445 | /* |
Bram Moolenaar | 2be7950 | 2014-08-13 21:58:28 +0200 | [diff] [blame] | 446 | * Prepare encryption for "buf" for the current key and method. |
| 447 | */ |
| 448 | static void |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 449 | ml_set_mfp_crypt(buf_T *buf) |
Bram Moolenaar | 2be7950 | 2014-08-13 21:58:28 +0200 | [diff] [blame] | 450 | { |
Yegappan Lakshmanan | e857598 | 2023-01-14 12:32:28 +0000 | [diff] [blame] | 451 | if (*buf->b_p_key == NUL) |
| 452 | return; |
Bram Moolenaar | 2be7950 | 2014-08-13 21:58:28 +0200 | [diff] [blame] | 453 | |
Yegappan Lakshmanan | e857598 | 2023-01-14 12:32:28 +0000 | [diff] [blame] | 454 | int method_nr = crypt_get_method_nr(buf); |
| 455 | |
| 456 | if (method_nr > CRYPT_M_ZIP && method_nr < CRYPT_M_SOD) |
| 457 | { |
| 458 | // Generate a seed and store it in the memfile. |
| 459 | sha2_seed(buf->b_ml.ml_mfp->mf_seed, MF_SEED_LEN, NULL, 0); |
Bram Moolenaar | 2be7950 | 2014-08-13 21:58:28 +0200 | [diff] [blame] | 460 | } |
Christian Brabandt | 19e6c4f | 2023-06-27 18:57:10 +0100 | [diff] [blame] | 461 | # ifdef FEAT_SODIUM |
Christian Brabandt | aae5834 | 2023-04-23 17:50:22 +0100 | [diff] [blame] | 462 | else if (crypt_method_is_sodium(method_nr)) |
Christian Brabandt | 19e6c4f | 2023-06-27 18:57:10 +0100 | [diff] [blame] | 463 | crypt_sodium_randombytes_buf(buf->b_ml.ml_mfp->mf_seed, MF_SEED_LEN); |
| 464 | # endif |
Bram Moolenaar | 2be7950 | 2014-08-13 21:58:28 +0200 | [diff] [blame] | 465 | } |
| 466 | |
| 467 | /* |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 468 | * Prepare encryption for "buf" with block 0 "b0p". |
Bram Moolenaar | e1b4822 | 2023-04-24 18:11:35 +0100 | [diff] [blame] | 469 | * Note: should not be called with libsodium encryption, since xchacha20 does |
| 470 | * not support swapfile encryption. |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 471 | */ |
| 472 | static void |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 473 | ml_set_b0_crypt(buf_T *buf, ZERO_BL *b0p) |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 474 | { |
| 475 | if (*buf->b_p_key == NUL) |
| 476 | b0p->b0_id[1] = BLOCK0_ID1; |
| 477 | else |
| 478 | { |
Bram Moolenaar | 8f4ac01 | 2014-08-10 13:38:34 +0200 | [diff] [blame] | 479 | int method_nr = crypt_get_method_nr(buf); |
| 480 | |
| 481 | b0p->b0_id[1] = id1_codes[method_nr]; |
Christian Brabandt | f573c6e | 2021-06-20 14:02:16 +0200 | [diff] [blame] | 482 | if (method_nr > CRYPT_M_ZIP && method_nr < CRYPT_M_SOD) |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 483 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 484 | // Generate a seed and store it in block 0 and in the memfile. |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 485 | sha2_seed(&b0p->b0_seed, MF_SEED_LEN, NULL, 0); |
| 486 | mch_memmove(buf->b_ml.ml_mfp->mf_seed, &b0p->b0_seed, MF_SEED_LEN); |
| 487 | } |
| 488 | } |
| 489 | } |
| 490 | |
| 491 | /* |
| 492 | * Called after the crypt key or 'cryptmethod' was changed for "buf". |
| 493 | * Will apply this to the swapfile. |
| 494 | * "old_key" is the previous key. It is equal to buf->b_p_key when |
| 495 | * 'cryptmethod' is changed. |
Bram Moolenaar | 49771f4 | 2010-07-20 17:32:38 +0200 | [diff] [blame] | 496 | * "old_cm" is the previous 'cryptmethod'. It is equal to the current |
| 497 | * 'cryptmethod' when 'key' is changed. |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 498 | */ |
| 499 | void |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 500 | ml_set_crypt_key( |
| 501 | buf_T *buf, |
| 502 | char_u *old_key, |
| 503 | char_u *old_cm) |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 504 | { |
| 505 | memfile_T *mfp = buf->b_ml.ml_mfp; |
| 506 | bhdr_T *hp; |
| 507 | int page_count; |
| 508 | int idx; |
| 509 | long error; |
| 510 | infoptr_T *ip; |
| 511 | PTR_BL *pp; |
| 512 | DATA_BL *dp; |
| 513 | blocknr_T bnum; |
| 514 | int top; |
Bram Moolenaar | bc56336 | 2015-06-09 18:35:25 +0200 | [diff] [blame] | 515 | int old_method; |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 516 | |
Christian Brabandt | f573c6e | 2021-06-20 14:02:16 +0200 | [diff] [blame] | 517 | if (mfp == NULL || mfp->mf_fd < 0) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 518 | return; // no memfile yet, nothing to do |
Bram Moolenaar | bc56336 | 2015-06-09 18:35:25 +0200 | [diff] [blame] | 519 | old_method = crypt_method_nr_from_name(old_cm); |
| 520 | |
Christian Brabandt | 19e6c4f | 2023-06-27 18:57:10 +0100 | [diff] [blame] | 521 | #ifdef FEAT_CRYPT |
| 522 | if (crypt_may_close_swapfile(buf, buf->b_p_key, crypt_get_method_nr(buf))) |
Christian Brabandt | f573c6e | 2021-06-20 14:02:16 +0200 | [diff] [blame] | 523 | return; |
Christian Brabandt | 19e6c4f | 2023-06-27 18:57:10 +0100 | [diff] [blame] | 524 | #endif |
Bram Moolenaar | e1b4822 | 2023-04-24 18:11:35 +0100 | [diff] [blame] | 525 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 526 | // First make sure the swapfile is in a consistent state, using the old |
| 527 | // key and method. |
Bram Moolenaar | bc56336 | 2015-06-09 18:35:25 +0200 | [diff] [blame] | 528 | { |
| 529 | char_u *new_key = buf->b_p_key; |
| 530 | char_u *new_buf_cm = buf->b_p_cm; |
| 531 | |
| 532 | buf->b_p_key = old_key; |
| 533 | buf->b_p_cm = old_cm; |
| 534 | ml_preserve(buf, FALSE); |
| 535 | buf->b_p_key = new_key; |
| 536 | buf->b_p_cm = new_buf_cm; |
| 537 | } |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 538 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 539 | // Set the key, method and seed to be used for reading, these must be the |
| 540 | // old values. |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 541 | mfp->mf_old_key = old_key; |
Bram Moolenaar | bc56336 | 2015-06-09 18:35:25 +0200 | [diff] [blame] | 542 | mfp->mf_old_cm = old_method; |
| 543 | if (old_method > 0 && *old_key != NUL) |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 544 | mch_memmove(mfp->mf_old_seed, mfp->mf_seed, MF_SEED_LEN); |
| 545 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 546 | // Update block 0 with the crypt flag and may set a new seed. |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 547 | ml_upd_block0(buf, UB_CRYPT); |
| 548 | |
| 549 | if (mfp->mf_infile_count > 2) |
| 550 | { |
| 551 | /* |
| 552 | * Need to read back all data blocks from disk, decrypt them with the |
| 553 | * old key/method and mark them to be written. The algorithm is |
| 554 | * similar to what happens in ml_recover(), but we skip negative block |
| 555 | * numbers. |
| 556 | */ |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 557 | ml_flush_line(buf); // flush buffered line |
| 558 | (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush locked block |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 559 | |
| 560 | hp = NULL; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 561 | bnum = 1; // start with block 1 |
| 562 | page_count = 1; // which is 1 page |
| 563 | idx = 0; // start with first index in block 1 |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 564 | error = 0; |
| 565 | buf->b_ml.ml_stack_top = 0; |
Bram Moolenaar | d23a823 | 2018-02-10 18:45:26 +0100 | [diff] [blame] | 566 | VIM_CLEAR(buf->b_ml.ml_stack); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 567 | buf->b_ml.ml_stack_size = 0; // no stack yet |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 568 | |
| 569 | for ( ; !got_int; line_breakcheck()) |
| 570 | { |
| 571 | if (hp != NULL) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 572 | mf_put(mfp, hp, FALSE, FALSE); // release previous block |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 573 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 574 | // get the block (pointer or data) |
=?UTF-8?q?Dundar=20G=C3=B6c?= | 420fabc | 2022-01-28 15:28:04 +0000 | [diff] [blame] | 575 | if ((hp = mf_get(mfp, bnum, page_count)) == NULL) |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 576 | { |
| 577 | if (bnum == 1) |
| 578 | break; |
| 579 | ++error; |
| 580 | } |
| 581 | else |
| 582 | { |
| 583 | pp = (PTR_BL *)(hp->bh_data); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 584 | if (pp->pb_id == PTR_ID) // it is a pointer block |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 585 | { |
| 586 | if (pp->pb_count == 0) |
| 587 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 588 | // empty block? |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 589 | ++error; |
| 590 | } |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 591 | else if (idx < (int)pp->pb_count) // go a block deeper |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 592 | { |
| 593 | if (pp->pb_pointer[idx].pe_bnum < 0) |
| 594 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 595 | // Skip data block with negative block number. |
| 596 | // Should not happen, because of the ml_preserve() |
| 597 | // above. Get same block again for next index. |
Bram Moolenaar | 4b7214e | 2019-01-03 21:55:32 +0100 | [diff] [blame] | 598 | ++idx; |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 599 | continue; |
| 600 | } |
| 601 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 602 | // going one block deeper in the tree, new entry in |
| 603 | // stack |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 604 | if ((top = ml_add_stack(buf)) < 0) |
| 605 | { |
| 606 | ++error; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 607 | break; // out of memory |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 608 | } |
| 609 | ip = &(buf->b_ml.ml_stack[top]); |
| 610 | ip->ip_bnum = bnum; |
| 611 | ip->ip_index = idx; |
| 612 | |
| 613 | bnum = pp->pb_pointer[idx].pe_bnum; |
| 614 | page_count = pp->pb_pointer[idx].pe_page_count; |
Bram Moolenaar | bc56336 | 2015-06-09 18:35:25 +0200 | [diff] [blame] | 615 | idx = 0; |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 616 | continue; |
| 617 | } |
| 618 | } |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 619 | else // not a pointer block |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 620 | { |
| 621 | dp = (DATA_BL *)(hp->bh_data); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 622 | if (dp->db_id != DATA_ID) // block id wrong |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 623 | ++error; |
| 624 | else |
| 625 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 626 | // It is a data block, need to write it back to disk. |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 627 | mf_put(mfp, hp, TRUE, FALSE); |
| 628 | hp = NULL; |
| 629 | } |
| 630 | } |
| 631 | } |
| 632 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 633 | if (buf->b_ml.ml_stack_top == 0) // finished |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 634 | break; |
| 635 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 636 | // go one block up in the tree |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 637 | ip = &(buf->b_ml.ml_stack[--(buf->b_ml.ml_stack_top)]); |
| 638 | bnum = ip->ip_bnum; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 639 | idx = ip->ip_index + 1; // go to next index |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 640 | page_count = 1; |
| 641 | } |
Bram Moolenaar | bc56336 | 2015-06-09 18:35:25 +0200 | [diff] [blame] | 642 | if (hp != NULL) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 643 | mf_put(mfp, hp, FALSE, FALSE); // release previous block |
Bram Moolenaar | 2e2e13c | 2010-12-08 13:17:03 +0100 | [diff] [blame] | 644 | |
| 645 | if (error > 0) |
Bram Moolenaar | 9d00e4a | 2022-01-05 17:49:15 +0000 | [diff] [blame] | 646 | emsg(_(e_error_while_updating_swap_file_crypt)); |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 647 | } |
| 648 | |
| 649 | mfp->mf_old_key = NULL; |
| 650 | } |
| 651 | #endif |
| 652 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 653 | /* |
| 654 | * ml_setname() is called when the file name of "buf" has been changed. |
| 655 | * It may rename the swap file. |
| 656 | */ |
| 657 | void |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 658 | ml_setname(buf_T *buf) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 659 | { |
| 660 | int success = FALSE; |
| 661 | memfile_T *mfp; |
| 662 | char_u *fname; |
| 663 | char_u *dirp; |
Bram Moolenaar | 48e330a | 2016-02-23 14:53:34 +0100 | [diff] [blame] | 664 | #if defined(MSWIN) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 665 | char_u *p; |
| 666 | #endif |
| 667 | |
| 668 | mfp = buf->b_ml.ml_mfp; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 669 | if (mfp->mf_fd < 0) // there is no swap file yet |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 670 | { |
| 671 | /* |
| 672 | * When 'updatecount' is 0 and 'noswapfile' there is no swap file. |
| 673 | * For help files we will make a swap file now. |
| 674 | */ |
Bram Moolenaar | e100440 | 2020-10-24 20:49:43 +0200 | [diff] [blame] | 675 | if (p_uc != 0 && (cmdmod.cmod_flags & CMOD_NOSWAPFILE) == 0) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 676 | ml_open_file(buf); // create a swap file |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 677 | return; |
| 678 | } |
| 679 | |
| 680 | /* |
| 681 | * Try all directories in the 'directory' option. |
| 682 | */ |
| 683 | dirp = p_dir; |
| 684 | for (;;) |
| 685 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 686 | if (*dirp == NUL) // tried all directories, fail |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 687 | break; |
Bram Moolenaar | 8fc061c | 2004-12-29 21:03:02 +0000 | [diff] [blame] | 688 | fname = findswapname(buf, &dirp, mfp->mf_fname); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 689 | // alloc's fname |
| 690 | if (dirp == NULL) // out of memory |
Bram Moolenaar | f541c36 | 2011-10-26 11:44:18 +0200 | [diff] [blame] | 691 | break; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 692 | if (fname == NULL) // no file name found for this dir |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 693 | continue; |
| 694 | |
Bram Moolenaar | 48e330a | 2016-02-23 14:53:34 +0100 | [diff] [blame] | 695 | #if defined(MSWIN) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 696 | /* |
| 697 | * Set full pathname for swap file now, because a ":!cd dir" may |
| 698 | * change directory without us knowing it. |
| 699 | */ |
| 700 | p = FullName_save(fname, FALSE); |
| 701 | vim_free(fname); |
| 702 | fname = p; |
| 703 | if (fname == NULL) |
| 704 | continue; |
| 705 | #endif |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 706 | // if the file name is the same we don't have to do anything |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 707 | if (fnamecmp(fname, mfp->mf_fname) == 0) |
| 708 | { |
| 709 | vim_free(fname); |
| 710 | success = TRUE; |
| 711 | break; |
| 712 | } |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 713 | // need to close the swap file before renaming |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 714 | if (mfp->mf_fd >= 0) |
| 715 | { |
| 716 | close(mfp->mf_fd); |
| 717 | mfp->mf_fd = -1; |
| 718 | } |
| 719 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 720 | // try to rename the swap file |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 721 | if (vim_rename(mfp->mf_fname, fname) == 0) |
| 722 | { |
| 723 | success = TRUE; |
| 724 | vim_free(mfp->mf_fname); |
| 725 | mfp->mf_fname = fname; |
| 726 | vim_free(mfp->mf_ffname); |
Bram Moolenaar | 48e330a | 2016-02-23 14:53:34 +0100 | [diff] [blame] | 727 | #if defined(MSWIN) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 728 | mfp->mf_ffname = NULL; // mf_fname is full pathname already |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 729 | #else |
| 730 | mf_set_ffname(mfp); |
| 731 | #endif |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 732 | ml_upd_block0(buf, UB_SAME_DIR); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 733 | break; |
| 734 | } |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 735 | vim_free(fname); // this fname didn't work, try another |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 736 | } |
| 737 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 738 | if (mfp->mf_fd == -1) // need to (re)open the swap file |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 739 | { |
| 740 | mfp->mf_fd = mch_open((char *)mfp->mf_fname, O_RDWR | O_EXTRA, 0); |
| 741 | if (mfp->mf_fd < 0) |
| 742 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 743 | // could not (re)open the swap file, what can we do???? |
Bram Moolenaar | eaaac01 | 2022-01-02 17:00:40 +0000 | [diff] [blame] | 744 | emsg(_(e_oops_lost_the_swap_file)); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 745 | return; |
| 746 | } |
Bram Moolenaar | f05da21 | 2009-11-17 16:13:15 +0000 | [diff] [blame] | 747 | #ifdef HAVE_FD_CLOEXEC |
| 748 | { |
| 749 | int fdflags = fcntl(mfp->mf_fd, F_GETFD); |
| 750 | if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) |
Bram Moolenaar | fbc4b4d | 2016-02-07 15:14:01 +0100 | [diff] [blame] | 751 | (void)fcntl(mfp->mf_fd, F_SETFD, fdflags | FD_CLOEXEC); |
Bram Moolenaar | f05da21 | 2009-11-17 16:13:15 +0000 | [diff] [blame] | 752 | } |
| 753 | #endif |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 754 | } |
| 755 | if (!success) |
Bram Moolenaar | eaaac01 | 2022-01-02 17:00:40 +0000 | [diff] [blame] | 756 | emsg(_(e_could_not_rename_swap_file)); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 757 | } |
| 758 | |
| 759 | /* |
| 760 | * Open a file for the memfile for all buffers that are not readonly or have |
| 761 | * been modified. |
| 762 | * Used when 'updatecount' changes from zero to non-zero. |
| 763 | */ |
| 764 | void |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 765 | ml_open_files(void) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 766 | { |
| 767 | buf_T *buf; |
| 768 | |
Bram Moolenaar | 2932359 | 2016-07-24 22:04:11 +0200 | [diff] [blame] | 769 | FOR_ALL_BUFFERS(buf) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 770 | if (!buf->b_p_ro || buf->b_changed) |
| 771 | ml_open_file(buf); |
| 772 | } |
| 773 | |
| 774 | /* |
| 775 | * Open a swap file for an existing memfile, if there is no swap file yet. |
| 776 | * If we are unable to find a file name, mf_fname will be NULL |
| 777 | * and the memfile will be in memory only (no recovery possible). |
| 778 | */ |
| 779 | void |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 780 | ml_open_file(buf_T *buf) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 781 | { |
| 782 | memfile_T *mfp; |
| 783 | char_u *fname; |
| 784 | char_u *dirp; |
| 785 | |
| 786 | mfp = buf->b_ml.ml_mfp; |
Bram Moolenaar | e100440 | 2020-10-24 20:49:43 +0200 | [diff] [blame] | 787 | if (mfp == NULL || mfp->mf_fd >= 0 || !buf->b_p_swf |
| 788 | || (cmdmod.cmod_flags & CMOD_NOSWAPFILE)) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 789 | return; // nothing to do |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 790 | |
Bram Moolenaar | a1956f6 | 2006-03-12 22:18:00 +0000 | [diff] [blame] | 791 | #ifdef FEAT_SPELL |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 792 | // For a spell buffer use a temp file name. |
Bram Moolenaar | 4770d09 | 2006-01-12 23:22:24 +0000 | [diff] [blame] | 793 | if (buf->b_spell) |
| 794 | { |
Bram Moolenaar | e5c421c | 2015-03-31 13:33:08 +0200 | [diff] [blame] | 795 | fname = vim_tempname('s', FALSE); |
Bram Moolenaar | 4770d09 | 2006-01-12 23:22:24 +0000 | [diff] [blame] | 796 | if (fname != NULL) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 797 | (void)mf_open_file(mfp, fname); // consumes fname! |
Bram Moolenaar | 4770d09 | 2006-01-12 23:22:24 +0000 | [diff] [blame] | 798 | buf->b_may_swap = FALSE; |
| 799 | return; |
| 800 | } |
| 801 | #endif |
| 802 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 803 | /* |
| 804 | * Try all directories in 'directory' option. |
| 805 | */ |
| 806 | dirp = p_dir; |
| 807 | for (;;) |
| 808 | { |
| 809 | if (*dirp == NUL) |
| 810 | break; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 811 | // There is a small chance that between choosing the swap file name |
| 812 | // and creating it, another Vim creates the file. In that case the |
| 813 | // creation will fail and we will use another directory. |
| 814 | fname = findswapname(buf, &dirp, NULL); // allocates fname |
Bram Moolenaar | f541c36 | 2011-10-26 11:44:18 +0200 | [diff] [blame] | 815 | if (dirp == NULL) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 816 | break; // out of memory |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 817 | if (fname == NULL) |
| 818 | continue; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 819 | if (mf_open_file(mfp, fname) == OK) // consumes fname! |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 820 | { |
Bram Moolenaar | 3a2a60c | 2023-05-27 18:02:55 +0100 | [diff] [blame] | 821 | // don't sync yet in ml_sync_all() |
| 822 | mfp->mf_dirty = MF_DIRTY_YES_NOSYNC; |
Bram Moolenaar | 48e330a | 2016-02-23 14:53:34 +0100 | [diff] [blame] | 823 | #if defined(MSWIN) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 824 | /* |
| 825 | * set full pathname for swap file now, because a ":!cd dir" may |
| 826 | * change directory without us knowing it. |
| 827 | */ |
| 828 | mf_fullname(mfp); |
| 829 | #endif |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 830 | ml_upd_block0(buf, UB_SAME_DIR); |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 831 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 832 | // Flush block zero, so others can read it |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 833 | if (mf_sync(mfp, MFS_ZERO) == OK) |
Bram Moolenaar | c32840f | 2006-01-14 21:23:38 +0000 | [diff] [blame] | 834 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 835 | // Mark all blocks that should be in the swapfile as dirty. |
| 836 | // Needed for when the 'swapfile' option was reset, so that |
| 837 | // the swap file was deleted, and then on again. |
Bram Moolenaar | c32840f | 2006-01-14 21:23:38 +0000 | [diff] [blame] | 838 | mf_set_dirty(mfp); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 839 | break; |
Bram Moolenaar | c32840f | 2006-01-14 21:23:38 +0000 | [diff] [blame] | 840 | } |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 841 | // Writing block 0 failed: close the file and try another dir |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 842 | mf_close_file(buf, FALSE); |
| 843 | } |
| 844 | } |
| 845 | |
Bram Moolenaar | 00e192b | 2019-10-19 17:01:28 +0200 | [diff] [blame] | 846 | if (*p_dir != NUL && mfp->mf_fname == NULL) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 847 | { |
Bram Moolenaar | 13608d8 | 2022-08-29 15:06:50 +0100 | [diff] [blame] | 848 | need_wait_return = TRUE; // call wait_return() later |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 849 | ++no_wait_return; |
Bram Moolenaar | eaaac01 | 2022-01-02 17:00:40 +0000 | [diff] [blame] | 850 | (void)semsg(_(e_unable_to_open_swap_file_for_str_recovery_impossible), |
Bram Moolenaar | e1704ba | 2012-10-03 18:25:00 +0200 | [diff] [blame] | 851 | buf_spname(buf) != NULL ? buf_spname(buf) : buf->b_fname); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 852 | --no_wait_return; |
| 853 | } |
| 854 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 855 | // don't try to open a swap file again |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 856 | buf->b_may_swap = FALSE; |
| 857 | } |
| 858 | |
| 859 | /* |
| 860 | * If still need to create a swap file, and starting to edit a not-readonly |
| 861 | * file, or reading into an existing buffer, create a swap file now. |
| 862 | */ |
| 863 | void |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 864 | check_need_swap( |
Bram Moolenaar | 2f0f871 | 2018-08-21 18:50:18 +0200 | [diff] [blame] | 865 | int newfile) // reading file into new buffer |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 866 | { |
Bram Moolenaar | 2f0f871 | 2018-08-21 18:50:18 +0200 | [diff] [blame] | 867 | int old_msg_silent = msg_silent; // might be reset by an E325 message |
| 868 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 869 | if (curbuf->b_may_swap && (!curbuf->b_p_ro || !newfile)) |
| 870 | ml_open_file(curbuf); |
Bram Moolenaar | 2f0f871 | 2018-08-21 18:50:18 +0200 | [diff] [blame] | 871 | msg_silent = old_msg_silent; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 872 | } |
| 873 | |
| 874 | /* |
| 875 | * Close memline for buffer 'buf'. |
| 876 | * If 'del_file' is TRUE, delete the swap file |
| 877 | */ |
| 878 | void |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 879 | ml_close(buf_T *buf, int del_file) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 880 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 881 | if (buf->b_ml.ml_mfp == NULL) // not open |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 882 | return; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 883 | mf_close(buf->b_ml.ml_mfp, del_file); // close the .swp file |
Bram Moolenaar | fa4873c | 2022-06-30 22:13:59 +0100 | [diff] [blame] | 884 | if (buf->b_ml.ml_line_lnum != 0 |
| 885 | && (buf->b_ml.ml_flags & (ML_LINE_DIRTY | ML_ALLOCATED))) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 886 | vim_free(buf->b_ml.ml_line_ptr); |
| 887 | vim_free(buf->b_ml.ml_stack); |
| 888 | #ifdef FEAT_BYTEOFF |
Bram Moolenaar | d23a823 | 2018-02-10 18:45:26 +0100 | [diff] [blame] | 889 | VIM_CLEAR(buf->b_ml.ml_chunksize); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 890 | #endif |
| 891 | buf->b_ml.ml_mfp = NULL; |
| 892 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 893 | // Reset the "recovered" flag, give the ATTENTION prompt the next time |
| 894 | // this buffer is loaded. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 895 | buf->b_flags &= ~BF_RECOVERED; |
| 896 | } |
| 897 | |
| 898 | /* |
| 899 | * Close all existing memlines and memfiles. |
| 900 | * Only used when exiting. |
| 901 | * When 'del_file' is TRUE, delete the memfiles. |
Bram Moolenaar | 81bf708 | 2005-02-12 14:31:42 +0000 | [diff] [blame] | 902 | * But don't delete files that were ":preserve"d when we are POSIX compatible. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 903 | */ |
| 904 | void |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 905 | ml_close_all(int del_file) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 906 | { |
| 907 | buf_T *buf; |
| 908 | |
Bram Moolenaar | 2932359 | 2016-07-24 22:04:11 +0200 | [diff] [blame] | 909 | FOR_ALL_BUFFERS(buf) |
Bram Moolenaar | 81bf708 | 2005-02-12 14:31:42 +0000 | [diff] [blame] | 910 | ml_close(buf, del_file && ((buf->b_flags & BF_PRESERVED) == 0 |
| 911 | || vim_strchr(p_cpo, CPO_PRESERVE) == NULL)); |
Bram Moolenaar | 34b466e | 2013-11-28 17:41:46 +0100 | [diff] [blame] | 912 | #ifdef FEAT_SPELL |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 913 | spell_delete_wordlist(); // delete the internal wordlist |
Bram Moolenaar | 34b466e | 2013-11-28 17:41:46 +0100 | [diff] [blame] | 914 | #endif |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 915 | #ifdef TEMPDIRNAMES |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 916 | vim_deltempdir(); // delete created temp directory |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 917 | #endif |
| 918 | } |
| 919 | |
| 920 | /* |
| 921 | * Close all memfiles for not modified buffers. |
| 922 | * Only use just before exiting! |
| 923 | */ |
| 924 | void |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 925 | ml_close_notmod(void) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 926 | { |
| 927 | buf_T *buf; |
| 928 | |
Bram Moolenaar | 2932359 | 2016-07-24 22:04:11 +0200 | [diff] [blame] | 929 | FOR_ALL_BUFFERS(buf) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 930 | if (!bufIsChanged(buf)) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 931 | ml_close(buf, TRUE); // close all not-modified buffers |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 932 | } |
| 933 | |
| 934 | /* |
| 935 | * Update the timestamp in the .swp file. |
| 936 | * Used when the file has been written. |
| 937 | */ |
| 938 | void |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 939 | ml_timestamp(buf_T *buf) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 940 | { |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 941 | ml_upd_block0(buf, UB_FNAME); |
| 942 | } |
| 943 | |
| 944 | /* |
| 945 | * Return FAIL when the ID of "b0p" is wrong. |
| 946 | */ |
| 947 | static int |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 948 | ml_check_b0_id(ZERO_BL *b0p) |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 949 | { |
| 950 | if (b0p->b0_id[0] != BLOCK0_ID0 |
| 951 | || (b0p->b0_id[1] != BLOCK0_ID1 |
| 952 | && b0p->b0_id[1] != BLOCK0_ID1_C0 |
Bram Moolenaar | 8f4ac01 | 2014-08-10 13:38:34 +0200 | [diff] [blame] | 953 | && b0p->b0_id[1] != BLOCK0_ID1_C1 |
Christian Brabandt | f573c6e | 2021-06-20 14:02:16 +0200 | [diff] [blame] | 954 | && b0p->b0_id[1] != BLOCK0_ID1_C2 |
Bram Moolenaar | 3a2a60c | 2023-05-27 18:02:55 +0100 | [diff] [blame] | 955 | && b0p->b0_id[1] != BLOCK0_ID1_C3 |
| 956 | && b0p->b0_id[1] != BLOCK0_ID1_C4) |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 957 | ) |
| 958 | return FAIL; |
| 959 | return OK; |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 960 | } |
| 961 | |
| 962 | /* |
| 963 | * Update the timestamp or the B0_SAME_DIR flag of the .swp file. |
| 964 | */ |
| 965 | static void |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 966 | ml_upd_block0(buf_T *buf, upd_block0_T what) |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 967 | { |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 968 | memfile_T *mfp; |
| 969 | bhdr_T *hp; |
| 970 | ZERO_BL *b0p; |
| 971 | |
| 972 | mfp = buf->b_ml.ml_mfp; |
Bram Moolenaar | 2be7950 | 2014-08-13 21:58:28 +0200 | [diff] [blame] | 973 | if (mfp == NULL) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 974 | return; |
Bram Moolenaar | 2be7950 | 2014-08-13 21:58:28 +0200 | [diff] [blame] | 975 | hp = mf_get(mfp, (blocknr_T)0, 1); |
| 976 | if (hp == NULL) |
| 977 | { |
| 978 | #ifdef FEAT_CRYPT |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 979 | // Possibly update the seed in the memfile before there is a block0. |
Bram Moolenaar | 2be7950 | 2014-08-13 21:58:28 +0200 | [diff] [blame] | 980 | if (what == UB_CRYPT) |
| 981 | ml_set_mfp_crypt(buf); |
| 982 | #endif |
| 983 | return; |
| 984 | } |
| 985 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 986 | b0p = (ZERO_BL *)(hp->bh_data); |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 987 | if (ml_check_b0_id(b0p) == FAIL) |
RestorerZ | 68ebcee | 2023-05-31 17:12:14 +0100 | [diff] [blame] | 988 | iemsg(e_ml_upd_block0_didnt_get_block_zero); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 989 | else |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 990 | { |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 991 | if (what == UB_FNAME) |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 992 | set_b0_fname(b0p, buf); |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 993 | #ifdef FEAT_CRYPT |
| 994 | else if (what == UB_CRYPT) |
| 995 | ml_set_b0_crypt(buf, b0p); |
| 996 | #endif |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 997 | else // what == UB_SAME_DIR |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 998 | set_b0_dir_flag(b0p, buf); |
| 999 | } |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1000 | mf_put(mfp, hp, TRUE, FALSE); |
| 1001 | } |
| 1002 | |
| 1003 | /* |
| 1004 | * Write file name and timestamp into block 0 of a swap file. |
| 1005 | * Also set buf->b_mtime. |
| 1006 | * Don't use NameBuff[]!!! |
| 1007 | */ |
| 1008 | static void |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 1009 | set_b0_fname(ZERO_BL *b0p, buf_T *buf) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1010 | { |
Bram Moolenaar | 8767f52 | 2016-07-01 17:17:39 +0200 | [diff] [blame] | 1011 | stat_T st; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1012 | |
| 1013 | if (buf->b_ffname == NULL) |
| 1014 | b0p->b0_fname[0] = NUL; |
| 1015 | else |
| 1016 | { |
Bram Moolenaar | 48e330a | 2016-02-23 14:53:34 +0100 | [diff] [blame] | 1017 | #if defined(MSWIN) || defined(AMIGA) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1018 | // Systems that cannot translate "~user" back into a path: copy the |
| 1019 | // file name unmodified. Do use slashes instead of backslashes for |
| 1020 | // portability. |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1021 | vim_strncpy(b0p->b0_fname, buf->b_ffname, B0_FNAME_SIZE_CRYPT - 1); |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 1022 | # ifdef BACKSLASH_IN_FILENAME |
| 1023 | forward_slash(b0p->b0_fname); |
| 1024 | # endif |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1025 | #else |
| 1026 | size_t flen, ulen; |
| 1027 | char_u uname[B0_UNAME_SIZE]; |
| 1028 | |
| 1029 | /* |
| 1030 | * For a file under the home directory of the current user, we try to |
| 1031 | * replace the home directory path with "~user". This helps when |
| 1032 | * editing the same file on different machines over a network. |
| 1033 | * First replace home dir path with "~/" with home_replace(). |
| 1034 | * Then insert the user name to get "~user/". |
| 1035 | */ |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1036 | home_replace(NULL, buf->b_ffname, b0p->b0_fname, |
| 1037 | B0_FNAME_SIZE_CRYPT, TRUE); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1038 | if (b0p->b0_fname[0] == '~') |
| 1039 | { |
| 1040 | flen = STRLEN(b0p->b0_fname); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1041 | // If there is no user name or it is too long, don't use "~/" |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1042 | if (get_user_name(uname, B0_UNAME_SIZE) == FAIL |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1043 | || (ulen = STRLEN(uname)) + flen > B0_FNAME_SIZE_CRYPT - 1) |
| 1044 | vim_strncpy(b0p->b0_fname, buf->b_ffname, |
| 1045 | B0_FNAME_SIZE_CRYPT - 1); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1046 | else |
| 1047 | { |
| 1048 | mch_memmove(b0p->b0_fname + ulen + 1, b0p->b0_fname + 1, flen); |
| 1049 | mch_memmove(b0p->b0_fname + 1, uname, ulen); |
| 1050 | } |
| 1051 | } |
| 1052 | #endif |
| 1053 | if (mch_stat((char *)buf->b_ffname, &st) >= 0) |
| 1054 | { |
| 1055 | long_to_char((long)st.st_mtime, b0p->b0_mtime); |
| 1056 | #ifdef CHECK_INODE |
| 1057 | long_to_char((long)st.st_ino, b0p->b0_ino); |
| 1058 | #endif |
| 1059 | buf_store_time(buf, &st, buf->b_ffname); |
| 1060 | buf->b_mtime_read = buf->b_mtime; |
Leah Neukirchen | 0a7984a | 2021-10-14 21:27:55 +0100 | [diff] [blame] | 1061 | buf->b_mtime_read_ns = buf->b_mtime_ns; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1062 | } |
| 1063 | else |
| 1064 | { |
| 1065 | long_to_char(0L, b0p->b0_mtime); |
| 1066 | #ifdef CHECK_INODE |
| 1067 | long_to_char(0L, b0p->b0_ino); |
| 1068 | #endif |
| 1069 | buf->b_mtime = 0; |
Leah Neukirchen | 0a7984a | 2021-10-14 21:27:55 +0100 | [diff] [blame] | 1070 | buf->b_mtime_ns = 0; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1071 | buf->b_mtime_read = 0; |
Leah Neukirchen | 0a7984a | 2021-10-14 21:27:55 +0100 | [diff] [blame] | 1072 | buf->b_mtime_read_ns = 0; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1073 | buf->b_orig_size = 0; |
| 1074 | buf->b_orig_mode = 0; |
| 1075 | } |
| 1076 | } |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 1077 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1078 | // Also add the 'fileencoding' if there is room. |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 1079 | add_b0_fenc(b0p, curbuf); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1080 | } |
| 1081 | |
| 1082 | /* |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 1083 | * Update the B0_SAME_DIR flag of the swap file. It's set if the file and the |
| 1084 | * swapfile for "buf" are in the same directory. |
| 1085 | * This is fail safe: if we are not sure the directories are equal the flag is |
| 1086 | * not set. |
| 1087 | */ |
| 1088 | static void |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 1089 | set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf) |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 1090 | { |
| 1091 | if (same_directory(buf->b_ml.ml_mfp->mf_fname, buf->b_ffname)) |
| 1092 | b0p->b0_flags |= B0_SAME_DIR; |
| 1093 | else |
| 1094 | b0p->b0_flags &= ~B0_SAME_DIR; |
| 1095 | } |
| 1096 | |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 1097 | /* |
| 1098 | * When there is room, add the 'fileencoding' to block zero. |
| 1099 | */ |
| 1100 | static void |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 1101 | add_b0_fenc( |
| 1102 | ZERO_BL *b0p, |
| 1103 | buf_T *buf) |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 1104 | { |
| 1105 | int n; |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1106 | int size = B0_FNAME_SIZE_NOCRYPT; |
| 1107 | |
Bram Moolenaar | fc3abf4 | 2019-01-24 15:54:21 +0100 | [diff] [blame] | 1108 | #ifdef FEAT_CRYPT |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1109 | // Without encryption use the same offset as in Vim 7.2 to be compatible. |
| 1110 | // With encryption it's OK to move elsewhere, the swap file is not |
| 1111 | // compatible anyway. |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1112 | if (*buf->b_p_key != NUL) |
| 1113 | size = B0_FNAME_SIZE_CRYPT; |
Bram Moolenaar | fc3abf4 | 2019-01-24 15:54:21 +0100 | [diff] [blame] | 1114 | #endif |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 1115 | |
Bram Moolenaar | a93fa7e | 2006-04-17 22:14:47 +0000 | [diff] [blame] | 1116 | n = (int)STRLEN(buf->b_p_fenc); |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1117 | if ((int)STRLEN(b0p->b0_fname) + n + 1 > size) |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 1118 | b0p->b0_flags &= ~B0_HAS_FENC; |
| 1119 | else |
| 1120 | { |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1121 | mch_memmove((char *)b0p->b0_fname + size - n, |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 1122 | (char *)buf->b_p_fenc, (size_t)n); |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1123 | *(b0p->b0_fname + size - n - 1) = NUL; |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 1124 | b0p->b0_flags |= B0_HAS_FENC; |
| 1125 | } |
| 1126 | } |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 1127 | |
Bram Moolenaar | f52f060 | 2021-03-10 21:26:37 +0100 | [diff] [blame] | 1128 | #if defined(HAVE_SYS_SYSINFO_H) && defined(HAVE_SYSINFO_UPTIME) |
| 1129 | # include <sys/sysinfo.h> |
| 1130 | #endif |
| 1131 | |
=?UTF-8?q?Ola=20S=C3=B6der?= | 599a6e5 | 2021-07-06 20:15:46 +0200 | [diff] [blame] | 1132 | #if defined(UNIX) || defined(MSWIN) |
Bram Moolenaar | f52f060 | 2021-03-10 21:26:37 +0100 | [diff] [blame] | 1133 | /* |
| 1134 | * Return TRUE if the process with number "b0p->b0_pid" is still running. |
| 1135 | * "swap_fname" is the name of the swap file, if it's from before a reboot then |
| 1136 | * the result is FALSE; |
| 1137 | */ |
| 1138 | static int |
| 1139 | swapfile_process_running(ZERO_BL *b0p, char_u *swap_fname UNUSED) |
| 1140 | { |
Bram Moolenaar | e2982d6 | 2021-10-06 11:27:21 +0100 | [diff] [blame] | 1141 | #if defined(HAVE_SYSINFO) && defined(HAVE_SYSINFO_UPTIME) |
Bram Moolenaar | f52f060 | 2021-03-10 21:26:37 +0100 | [diff] [blame] | 1142 | stat_T st; |
| 1143 | struct sysinfo sinfo; |
| 1144 | |
| 1145 | // If the system rebooted after when the swap file was written then the |
| 1146 | // process can't be running now. |
| 1147 | if (mch_stat((char *)swap_fname, &st) != -1 |
| 1148 | && sysinfo(&sinfo) == 0 |
Bram Moolenaar | 23b32a8 | 2021-03-10 21:55:46 +0100 | [diff] [blame] | 1149 | && st.st_mtime < time(NULL) - ( |
=?UTF-8?q?Ola=20S=C3=B6der?= | 599a6e5 | 2021-07-06 20:15:46 +0200 | [diff] [blame] | 1150 | # ifdef FEAT_EVAL |
Bram Moolenaar | 23b32a8 | 2021-03-10 21:55:46 +0100 | [diff] [blame] | 1151 | override_sysinfo_uptime >= 0 ? override_sysinfo_uptime : |
=?UTF-8?q?Ola=20S=C3=B6der?= | 599a6e5 | 2021-07-06 20:15:46 +0200 | [diff] [blame] | 1152 | # endif |
Bram Moolenaar | 23b32a8 | 2021-03-10 21:55:46 +0100 | [diff] [blame] | 1153 | sinfo.uptime)) |
Bram Moolenaar | f52f060 | 2021-03-10 21:26:37 +0100 | [diff] [blame] | 1154 | return FALSE; |
=?UTF-8?q?Ola=20S=C3=B6der?= | 599a6e5 | 2021-07-06 20:15:46 +0200 | [diff] [blame] | 1155 | # endif |
Bram Moolenaar | f52f060 | 2021-03-10 21:26:37 +0100 | [diff] [blame] | 1156 | return mch_process_running(char_to_long(b0p->b0_pid)); |
| 1157 | } |
=?UTF-8?q?Ola=20S=C3=B6der?= | 599a6e5 | 2021-07-06 20:15:46 +0200 | [diff] [blame] | 1158 | #endif |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 1159 | |
| 1160 | /* |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1161 | * Try to recover curbuf from the .swp file. |
Bram Moolenaar | 99499b1 | 2019-05-23 21:35:48 +0200 | [diff] [blame] | 1162 | * If "checkext" is TRUE, check the extension and detect whether it is |
| 1163 | * a swap file. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1164 | */ |
| 1165 | void |
Bram Moolenaar | 99499b1 | 2019-05-23 21:35:48 +0200 | [diff] [blame] | 1166 | ml_recover(int checkext) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1167 | { |
| 1168 | buf_T *buf = NULL; |
| 1169 | memfile_T *mfp = NULL; |
| 1170 | char_u *fname; |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1171 | char_u *fname_used = NULL; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1172 | bhdr_T *hp = NULL; |
| 1173 | ZERO_BL *b0p; |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 1174 | int b0_ff; |
| 1175 | char_u *b0_fenc = NULL; |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1176 | #ifdef FEAT_CRYPT |
| 1177 | int b0_cm = -1; |
| 1178 | #endif |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1179 | PTR_BL *pp; |
| 1180 | DATA_BL *dp; |
| 1181 | infoptr_T *ip; |
| 1182 | blocknr_T bnum; |
| 1183 | int page_count; |
Bram Moolenaar | 8767f52 | 2016-07-01 17:17:39 +0200 | [diff] [blame] | 1184 | stat_T org_stat, swp_stat; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1185 | int len; |
| 1186 | int directly; |
| 1187 | linenr_T lnum; |
| 1188 | char_u *p; |
| 1189 | int i; |
| 1190 | long error; |
| 1191 | int cannot_open; |
| 1192 | linenr_T line_count; |
| 1193 | int has_error; |
| 1194 | int idx; |
| 1195 | int top; |
| 1196 | int txt_start; |
Bram Moolenaar | 8767f52 | 2016-07-01 17:17:39 +0200 | [diff] [blame] | 1197 | off_T size; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1198 | int called_from_main; |
| 1199 | int serious_error = TRUE; |
| 1200 | long mtime; |
| 1201 | int attr; |
Bram Moolenaar | fc2d5bd | 2010-05-15 17:06:53 +0200 | [diff] [blame] | 1202 | int orig_file_status = NOTDONE; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1203 | |
| 1204 | recoverymode = TRUE; |
| 1205 | called_from_main = (curbuf->b_ml.ml_mfp == NULL); |
Bram Moolenaar | 8820b48 | 2017-03-16 17:23:31 +0100 | [diff] [blame] | 1206 | attr = HL_ATTR(HLF_E); |
Bram Moolenaar | d0ba34a | 2009-11-03 12:06:23 +0000 | [diff] [blame] | 1207 | |
| 1208 | /* |
Bram Moolenaar | af903e5 | 2017-12-02 15:11:22 +0100 | [diff] [blame] | 1209 | * If the file name ends in ".s[a-w][a-z]" we assume this is the swap file. |
Bram Moolenaar | d0ba34a | 2009-11-03 12:06:23 +0000 | [diff] [blame] | 1210 | * Otherwise a search is done to find the swap file(s). |
| 1211 | */ |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1212 | fname = curbuf->b_fname; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1213 | if (fname == NULL) // When there is no file name |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1214 | fname = (char_u *)""; |
| 1215 | len = (int)STRLEN(fname); |
Bram Moolenaar | 99499b1 | 2019-05-23 21:35:48 +0200 | [diff] [blame] | 1216 | if (checkext && len >= 4 && |
Bram Moolenaar | e60acc1 | 2011-05-10 16:41:25 +0200 | [diff] [blame] | 1217 | #if defined(VMS) |
Bram Moolenaar | 79518e2 | 2017-02-17 16:31:35 +0100 | [diff] [blame] | 1218 | STRNICMP(fname + len - 4, "_s", 2) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1219 | #else |
Bram Moolenaar | 79518e2 | 2017-02-17 16:31:35 +0100 | [diff] [blame] | 1220 | STRNICMP(fname + len - 4, ".s", 2) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1221 | #endif |
Bram Moolenaar | 79518e2 | 2017-02-17 16:31:35 +0100 | [diff] [blame] | 1222 | == 0 |
Bram Moolenaar | af903e5 | 2017-12-02 15:11:22 +0100 | [diff] [blame] | 1223 | && vim_strchr((char_u *)"abcdefghijklmnopqrstuvw", |
| 1224 | TOLOWER_ASC(fname[len - 2])) != NULL |
Bram Moolenaar | d0ba34a | 2009-11-03 12:06:23 +0000 | [diff] [blame] | 1225 | && ASCII_ISALPHA(fname[len - 1])) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1226 | { |
| 1227 | directly = TRUE; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1228 | fname_used = vim_strsave(fname); // make a copy for mf_open() |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1229 | } |
| 1230 | else |
| 1231 | { |
| 1232 | directly = FALSE; |
| 1233 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1234 | // count the number of matching swap files |
Bram Moolenaar | c216a7a | 2022-12-05 13:50:55 +0000 | [diff] [blame] | 1235 | len = recover_names(fname, FALSE, NULL, 0, NULL); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1236 | if (len == 0) // no swap files found |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1237 | { |
Bram Moolenaar | eaaac01 | 2022-01-02 17:00:40 +0000 | [diff] [blame] | 1238 | semsg(_(e_no_swap_file_found_for_str), fname); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1239 | goto theend; |
| 1240 | } |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1241 | if (len == 1) // one swap file found, use it |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1242 | i = 1; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1243 | else // several swap files found, choose |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1244 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1245 | // list the names of the swap files |
Bram Moolenaar | c216a7a | 2022-12-05 13:50:55 +0000 | [diff] [blame] | 1246 | (void)recover_names(fname, TRUE, NULL, 0, NULL); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1247 | msg_putchar('\n'); |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 1248 | msg_puts(_("Enter number of swap file to use (0 to quit): ")); |
Bram Moolenaar | 24bbcfe | 2005-06-28 23:32:02 +0000 | [diff] [blame] | 1249 | i = get_number(FALSE, NULL); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1250 | if (i < 1 || i > len) |
| 1251 | goto theend; |
| 1252 | } |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1253 | // get the swap file name that will be used |
Bram Moolenaar | c216a7a | 2022-12-05 13:50:55 +0000 | [diff] [blame] | 1254 | (void)recover_names(fname, FALSE, NULL, i, &fname_used); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1255 | } |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1256 | if (fname_used == NULL) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1257 | goto theend; // out of memory |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1258 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1259 | // When called from main() still need to initialize storage structure |
Bram Moolenaar | 4770d09 | 2006-01-12 23:22:24 +0000 | [diff] [blame] | 1260 | if (called_from_main && ml_open(curbuf) == FAIL) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1261 | getout(1); |
| 1262 | |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1263 | /* |
| 1264 | * Allocate a buffer structure for the swap file that is used for recovery. |
Bram Moolenaar | 0ad014c | 2010-07-25 14:00:46 +0200 | [diff] [blame] | 1265 | * Only the memline and crypt information in it are really used. |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1266 | */ |
Bram Moolenaar | c799fe2 | 2019-05-28 23:08:19 +0200 | [diff] [blame] | 1267 | buf = ALLOC_ONE(buf_T); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1268 | if (buf == NULL) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1269 | goto theend; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1270 | |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1271 | /* |
| 1272 | * init fields in memline struct |
| 1273 | */ |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1274 | buf->b_ml.ml_stack_size = 0; // no stack yet |
| 1275 | buf->b_ml.ml_stack = NULL; // no stack yet |
| 1276 | buf->b_ml.ml_stack_top = 0; // nothing in the stack |
| 1277 | buf->b_ml.ml_line_lnum = 0; // no cached line |
| 1278 | buf->b_ml.ml_locked = NULL; // no locked block |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1279 | buf->b_ml.ml_flags = 0; |
Bram Moolenaar | 0fe849a | 2010-07-25 15:11:11 +0200 | [diff] [blame] | 1280 | #ifdef FEAT_CRYPT |
| 1281 | buf->b_p_key = empty_option; |
| 1282 | buf->b_p_cm = empty_option; |
| 1283 | #endif |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1284 | |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1285 | /* |
| 1286 | * open the memfile from the old swap file |
| 1287 | */ |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1288 | p = vim_strsave(fname_used); // save "fname_used" for the message: |
| 1289 | // mf_open() will consume "fname_used"! |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1290 | mfp = mf_open(fname_used, O_RDONLY); |
| 1291 | fname_used = p; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1292 | if (mfp == NULL || mfp->mf_fd < 0) |
| 1293 | { |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1294 | if (fname_used != NULL) |
Bram Moolenaar | eaaac01 | 2022-01-02 17:00:40 +0000 | [diff] [blame] | 1295 | semsg(_(e_cannot_open_str), fname_used); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1296 | goto theend; |
| 1297 | } |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1298 | buf->b_ml.ml_mfp = mfp; |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1299 | #ifdef FEAT_CRYPT |
| 1300 | mfp->mf_buffer = buf; |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1301 | #endif |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1302 | |
| 1303 | /* |
| 1304 | * The page size set in mf_open() might be different from the page size |
| 1305 | * used in the swap file, we must get it from block 0. But to read block |
| 1306 | * 0 we need a page size. Use the minimal size for block 0 here, it will |
| 1307 | * be set to the real value below. |
| 1308 | */ |
| 1309 | mfp->mf_page_size = MIN_SWAP_PAGE_SIZE; |
| 1310 | |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1311 | /* |
| 1312 | * try to read block 0 |
| 1313 | */ |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1314 | if ((hp = mf_get(mfp, (blocknr_T)0, 1)) == NULL) |
| 1315 | { |
| 1316 | msg_start(); |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 1317 | msg_puts_attr(_("Unable to read block 0 from "), attr | MSG_HIST); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1318 | msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST); |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 1319 | msg_puts_attr(_("\nMaybe no changes were made or Vim did not update the swap file."), |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1320 | attr | MSG_HIST); |
| 1321 | msg_end(); |
| 1322 | goto theend; |
| 1323 | } |
| 1324 | b0p = (ZERO_BL *)(hp->bh_data); |
| 1325 | if (STRNCMP(b0p->b0_version, "VIM 3.0", 7) == 0) |
| 1326 | { |
| 1327 | msg_start(); |
| 1328 | msg_outtrans_attr(mfp->mf_fname, MSG_HIST); |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 1329 | msg_puts_attr(_(" cannot be used with this version of Vim.\n"), |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1330 | MSG_HIST); |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 1331 | msg_puts_attr(_("Use Vim version 3.0.\n"), MSG_HIST); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1332 | msg_end(); |
| 1333 | goto theend; |
| 1334 | } |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1335 | if (ml_check_b0_id(b0p) == FAIL) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1336 | { |
Bram Moolenaar | eaaac01 | 2022-01-02 17:00:40 +0000 | [diff] [blame] | 1337 | semsg(_(e_str_does_not_look_like_vim_swap_file), mfp->mf_fname); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1338 | goto theend; |
| 1339 | } |
| 1340 | if (b0_magic_wrong(b0p)) |
| 1341 | { |
| 1342 | msg_start(); |
| 1343 | msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST); |
Bram Moolenaar | 48e330a | 2016-02-23 14:53:34 +0100 | [diff] [blame] | 1344 | #if defined(MSWIN) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1345 | if (STRNCMP(b0p->b0_hname, "PC ", 3) == 0) |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 1346 | msg_puts_attr(_(" cannot be used with this version of Vim.\n"), |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1347 | attr | MSG_HIST); |
| 1348 | else |
| 1349 | #endif |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 1350 | msg_puts_attr(_(" cannot be used on this computer.\n"), |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1351 | attr | MSG_HIST); |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 1352 | msg_puts_attr(_("The file was created on "), attr | MSG_HIST); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1353 | // avoid going past the end of a corrupted hostname |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1354 | b0p->b0_fname[0] = NUL; |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 1355 | msg_puts_attr((char *)b0p->b0_hname, attr | MSG_HIST); |
| 1356 | msg_puts_attr(_(",\nor the file has been damaged."), attr | MSG_HIST); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1357 | msg_end(); |
| 1358 | goto theend; |
| 1359 | } |
Bram Moolenaar | 1c53628 | 2007-04-26 15:21:56 +0000 | [diff] [blame] | 1360 | |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1361 | #ifdef FEAT_CRYPT |
K.Takata | eeec254 | 2021-06-02 13:28:16 +0200 | [diff] [blame] | 1362 | for (i = 0; i < (int)ARRAY_LENGTH(id1_codes); ++i) |
Bram Moolenaar | 8f4ac01 | 2014-08-10 13:38:34 +0200 | [diff] [blame] | 1363 | if (id1_codes[i] == b0p->b0_id[1]) |
| 1364 | b0_cm = i; |
| 1365 | if (b0_cm > 0) |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1366 | mch_memmove(mfp->mf_seed, &b0p->b0_seed, MF_SEED_LEN); |
Bram Moolenaar | 8f4ac01 | 2014-08-10 13:38:34 +0200 | [diff] [blame] | 1367 | crypt_set_cm_option(buf, b0_cm < 0 ? 0 : b0_cm); |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1368 | #else |
| 1369 | if (b0p->b0_id[1] != BLOCK0_ID1) |
| 1370 | { |
Bram Moolenaar | 9d00e4a | 2022-01-05 17:49:15 +0000 | [diff] [blame] | 1371 | semsg(_(e_str_is_encrypted_and_this_version_of_vim_does_not_support_encryption), mfp->mf_fname); |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1372 | goto theend; |
| 1373 | } |
| 1374 | #endif |
| 1375 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1376 | /* |
| 1377 | * If we guessed the wrong page size, we have to recalculate the |
| 1378 | * highest block number in the file. |
| 1379 | */ |
| 1380 | if (mfp->mf_page_size != (unsigned)char_to_long(b0p->b0_page_size)) |
| 1381 | { |
Bram Moolenaar | 1c53628 | 2007-04-26 15:21:56 +0000 | [diff] [blame] | 1382 | unsigned previous_page_size = mfp->mf_page_size; |
| 1383 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1384 | mf_new_page_size(mfp, (unsigned)char_to_long(b0p->b0_page_size)); |
Bram Moolenaar | 1c53628 | 2007-04-26 15:21:56 +0000 | [diff] [blame] | 1385 | if (mfp->mf_page_size < previous_page_size) |
| 1386 | { |
| 1387 | msg_start(); |
| 1388 | msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST); |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 1389 | msg_puts_attr(_(" has been damaged (page size is smaller than minimum value).\n"), |
Bram Moolenaar | 1c53628 | 2007-04-26 15:21:56 +0000 | [diff] [blame] | 1390 | attr | MSG_HIST); |
| 1391 | msg_end(); |
| 1392 | goto theend; |
| 1393 | } |
Bram Moolenaar | 8767f52 | 2016-07-01 17:17:39 +0200 | [diff] [blame] | 1394 | if ((size = vim_lseek(mfp->mf_fd, (off_T)0L, SEEK_END)) <= 0) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1395 | mfp->mf_blocknr_max = 0; // no file or empty file |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1396 | else |
| 1397 | mfp->mf_blocknr_max = (blocknr_T)(size / mfp->mf_page_size); |
| 1398 | mfp->mf_infile_count = mfp->mf_blocknr_max; |
Bram Moolenaar | 1c53628 | 2007-04-26 15:21:56 +0000 | [diff] [blame] | 1399 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1400 | // need to reallocate the memory used to store the data |
Bram Moolenaar | 1c53628 | 2007-04-26 15:21:56 +0000 | [diff] [blame] | 1401 | p = alloc(mfp->mf_page_size); |
| 1402 | if (p == NULL) |
| 1403 | goto theend; |
| 1404 | mch_memmove(p, hp->bh_data, previous_page_size); |
| 1405 | vim_free(hp->bh_data); |
| 1406 | hp->bh_data = p; |
| 1407 | b0p = (ZERO_BL *)(hp->bh_data); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1408 | } |
| 1409 | |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1410 | /* |
| 1411 | * If .swp file name given directly, use name from swap file for buffer. |
| 1412 | */ |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1413 | if (directly) |
| 1414 | { |
| 1415 | expand_env(b0p->b0_fname, NameBuff, MAXPATHL); |
| 1416 | if (setfname(curbuf, NameBuff, NULL, TRUE) == FAIL) |
| 1417 | goto theend; |
| 1418 | } |
| 1419 | |
| 1420 | home_replace(NULL, mfp->mf_fname, NameBuff, MAXPATHL, TRUE); |
Bram Moolenaar | f9e3e09 | 2019-01-13 23:38:42 +0100 | [diff] [blame] | 1421 | smsg(_("Using swap file \"%s\""), NameBuff); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1422 | |
| 1423 | if (buf_spname(curbuf) != NULL) |
Bram Moolenaar | e1704ba | 2012-10-03 18:25:00 +0200 | [diff] [blame] | 1424 | vim_strncpy(NameBuff, buf_spname(curbuf), MAXPATHL - 1); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1425 | else |
| 1426 | home_replace(NULL, curbuf->b_ffname, NameBuff, MAXPATHL, TRUE); |
Bram Moolenaar | f9e3e09 | 2019-01-13 23:38:42 +0100 | [diff] [blame] | 1427 | smsg(_("Original file \"%s\""), NameBuff); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1428 | msg_putchar('\n'); |
| 1429 | |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1430 | /* |
| 1431 | * check date of swap file and original file |
| 1432 | */ |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1433 | mtime = char_to_long(b0p->b0_mtime); |
| 1434 | if (curbuf->b_ffname != NULL |
| 1435 | && mch_stat((char *)curbuf->b_ffname, &org_stat) != -1 |
| 1436 | && ((mch_stat((char *)mfp->mf_fname, &swp_stat) != -1 |
| 1437 | && org_stat.st_mtime > swp_stat.st_mtime) |
| 1438 | || org_stat.st_mtime != mtime)) |
Bram Moolenaar | eaaac01 | 2022-01-02 17:00:40 +0000 | [diff] [blame] | 1439 | emsg(_(e_warning_original_file_may_have_been_changed)); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1440 | out_flush(); |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 1441 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1442 | // Get the 'fileformat' and 'fileencoding' from block zero. |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 1443 | b0_ff = (b0p->b0_flags & B0_FF_MASK); |
| 1444 | if (b0p->b0_flags & B0_HAS_FENC) |
| 1445 | { |
Bram Moolenaar | f506c5b | 2010-06-22 06:28:58 +0200 | [diff] [blame] | 1446 | int fnsize = B0_FNAME_SIZE_NOCRYPT; |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1447 | |
| 1448 | #ifdef FEAT_CRYPT |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1449 | // Use the same size as in add_b0_fenc(). |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1450 | if (b0p->b0_id[1] != BLOCK0_ID1) |
Bram Moolenaar | f506c5b | 2010-06-22 06:28:58 +0200 | [diff] [blame] | 1451 | fnsize = B0_FNAME_SIZE_CRYPT; |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1452 | #endif |
Bram Moolenaar | f506c5b | 2010-06-22 06:28:58 +0200 | [diff] [blame] | 1453 | for (p = b0p->b0_fname + fnsize; p > b0p->b0_fname && p[-1] != NUL; --p) |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 1454 | ; |
Bram Moolenaar | 71ccd03 | 2020-06-12 22:59:11 +0200 | [diff] [blame] | 1455 | b0_fenc = vim_strnsave(p, b0p->b0_fname + fnsize - p); |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 1456 | } |
| 1457 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1458 | mf_put(mfp, hp, FALSE, FALSE); // release block 0 |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1459 | hp = NULL; |
| 1460 | |
| 1461 | /* |
| 1462 | * Now that we are sure that the file is going to be recovered, clear the |
| 1463 | * contents of the current buffer. |
| 1464 | */ |
| 1465 | while (!(curbuf->b_ml.ml_flags & ML_EMPTY)) |
Bram Moolenaar | ca70c07 | 2020-05-30 20:30:46 +0200 | [diff] [blame] | 1466 | ml_delete((linenr_T)1); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1467 | |
| 1468 | /* |
| 1469 | * Try reading the original file to obtain the values of 'fileformat', |
| 1470 | * 'fileencoding', etc. Ignore errors. The text itself is not used. |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1471 | * When the file is encrypted the user is asked to enter the key. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1472 | */ |
| 1473 | if (curbuf->b_ffname != NULL) |
Bram Moolenaar | fc2d5bd | 2010-05-15 17:06:53 +0200 | [diff] [blame] | 1474 | orig_file_status = readfile(curbuf->b_ffname, NULL, (linenr_T)0, |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1475 | (linenr_T)0, (linenr_T)MAXLNUM, NULL, READ_NEW); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1476 | |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1477 | #ifdef FEAT_CRYPT |
| 1478 | if (b0_cm >= 0) |
| 1479 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1480 | // Need to ask the user for the crypt key. If this fails we continue |
| 1481 | // without a key, will probably get garbage text. |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1482 | if (*curbuf->b_p_key != NUL) |
| 1483 | { |
Bram Moolenaar | f9e3e09 | 2019-01-13 23:38:42 +0100 | [diff] [blame] | 1484 | smsg(_("Swap file is encrypted: \"%s\""), fname_used); |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 1485 | msg_puts(_("\nIf you entered a new crypt key but did not write the text file,")); |
| 1486 | msg_puts(_("\nenter the new crypt key.")); |
| 1487 | msg_puts(_("\nIf you wrote the text file after changing the crypt key press enter")); |
| 1488 | msg_puts(_("\nto use the same key for text file and swap file")); |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1489 | } |
| 1490 | else |
Bram Moolenaar | f9e3e09 | 2019-01-13 23:38:42 +0100 | [diff] [blame] | 1491 | smsg(_(need_key_msg), fname_used); |
Bram Moolenaar | 8f4ac01 | 2014-08-10 13:38:34 +0200 | [diff] [blame] | 1492 | buf->b_p_key = crypt_get_key(FALSE, FALSE); |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1493 | if (buf->b_p_key == NULL) |
| 1494 | buf->b_p_key = curbuf->b_p_key; |
| 1495 | else if (*buf->b_p_key == NUL) |
| 1496 | { |
| 1497 | vim_free(buf->b_p_key); |
| 1498 | buf->b_p_key = curbuf->b_p_key; |
| 1499 | } |
| 1500 | if (buf->b_p_key == NULL) |
| 1501 | buf->b_p_key = empty_option; |
| 1502 | } |
| 1503 | #endif |
| 1504 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1505 | // Use the 'fileformat' and 'fileencoding' as stored in the swap file. |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 1506 | if (b0_ff != 0) |
| 1507 | set_fileformat(b0_ff - 1, OPT_LOCAL); |
| 1508 | if (b0_fenc != NULL) |
| 1509 | { |
Bram Moolenaar | 31e5c60 | 2022-04-15 13:53:33 +0100 | [diff] [blame] | 1510 | set_option_value_give_err((char_u *)"fenc", 0L, b0_fenc, OPT_LOCAL); |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 1511 | vim_free(b0_fenc); |
| 1512 | } |
Bram Moolenaar | c024b46 | 2019-06-08 18:07:21 +0200 | [diff] [blame] | 1513 | unchanged(curbuf, TRUE, TRUE); |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 1514 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1515 | bnum = 1; // start with block 1 |
| 1516 | page_count = 1; // which is 1 page |
| 1517 | lnum = 0; // append after line 0 in curbuf |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1518 | line_count = 0; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1519 | idx = 0; // start with first index in block 1 |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1520 | error = 0; |
| 1521 | buf->b_ml.ml_stack_top = 0; |
| 1522 | buf->b_ml.ml_stack = NULL; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1523 | buf->b_ml.ml_stack_size = 0; // no stack yet |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1524 | |
| 1525 | if (curbuf->b_ffname == NULL) |
| 1526 | cannot_open = TRUE; |
| 1527 | else |
| 1528 | cannot_open = FALSE; |
| 1529 | |
| 1530 | serious_error = FALSE; |
| 1531 | for ( ; !got_int; line_breakcheck()) |
| 1532 | { |
| 1533 | if (hp != NULL) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1534 | mf_put(mfp, hp, FALSE, FALSE); // release previous block |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1535 | |
| 1536 | /* |
| 1537 | * get block |
| 1538 | */ |
=?UTF-8?q?Dundar=20G=C3=B6c?= | 420fabc | 2022-01-28 15:28:04 +0000 | [diff] [blame] | 1539 | if ((hp = mf_get(mfp, bnum, page_count)) == NULL) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1540 | { |
| 1541 | if (bnum == 1) |
| 1542 | { |
Bram Moolenaar | eaaac01 | 2022-01-02 17:00:40 +0000 | [diff] [blame] | 1543 | semsg(_(e_unable_to_read_block_one_from_str), mfp->mf_fname); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1544 | goto theend; |
| 1545 | } |
| 1546 | ++error; |
| 1547 | ml_append(lnum++, (char_u *)_("???MANY LINES MISSING"), |
| 1548 | (colnr_T)0, TRUE); |
| 1549 | } |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1550 | else // there is a block |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1551 | { |
| 1552 | pp = (PTR_BL *)(hp->bh_data); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1553 | if (pp->pb_id == PTR_ID) // it is a pointer block |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1554 | { |
Bram Moolenaar | b67ba03 | 2023-04-22 21:14:26 +0100 | [diff] [blame] | 1555 | int ptr_block_error = FALSE; |
| 1556 | if (pp->pb_count_max != PB_COUNT_MAX(mfp)) |
| 1557 | { |
| 1558 | ptr_block_error = TRUE; |
| 1559 | pp->pb_count_max = PB_COUNT_MAX(mfp); |
| 1560 | } |
| 1561 | if (pp->pb_count > pp->pb_count_max) |
| 1562 | { |
| 1563 | ptr_block_error = TRUE; |
| 1564 | pp->pb_count = pp->pb_count_max; |
| 1565 | } |
| 1566 | if (ptr_block_error) |
| 1567 | emsg(_(e_warning_pointer_block_corrupted)); |
| 1568 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1569 | // check line count when using pointer block first time |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1570 | if (idx == 0 && line_count != 0) |
| 1571 | { |
| 1572 | for (i = 0; i < (int)pp->pb_count; ++i) |
| 1573 | line_count -= pp->pb_pointer[i].pe_line_count; |
| 1574 | if (line_count != 0) |
| 1575 | { |
| 1576 | ++error; |
| 1577 | ml_append(lnum++, (char_u *)_("???LINE COUNT WRONG"), |
| 1578 | (colnr_T)0, TRUE); |
| 1579 | } |
| 1580 | } |
| 1581 | |
| 1582 | if (pp->pb_count == 0) |
| 1583 | { |
| 1584 | ml_append(lnum++, (char_u *)_("???EMPTY BLOCK"), |
| 1585 | (colnr_T)0, TRUE); |
| 1586 | ++error; |
| 1587 | } |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1588 | else if (idx < (int)pp->pb_count) // go a block deeper |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1589 | { |
| 1590 | if (pp->pb_pointer[idx].pe_bnum < 0) |
| 1591 | { |
| 1592 | /* |
| 1593 | * Data block with negative block number. |
| 1594 | * Try to read lines from the original file. |
| 1595 | * This is slow, but it works. |
| 1596 | */ |
| 1597 | if (!cannot_open) |
| 1598 | { |
| 1599 | line_count = pp->pb_pointer[idx].pe_line_count; |
| 1600 | if (readfile(curbuf->b_ffname, NULL, lnum, |
| 1601 | pp->pb_pointer[idx].pe_old_lnum - 1, |
Bram Moolenaar | e13b9af | 2017-01-13 22:01:02 +0100 | [diff] [blame] | 1602 | line_count, NULL, 0) != OK) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1603 | cannot_open = TRUE; |
| 1604 | else |
| 1605 | lnum += line_count; |
| 1606 | } |
| 1607 | if (cannot_open) |
| 1608 | { |
| 1609 | ++error; |
| 1610 | ml_append(lnum++, (char_u *)_("???LINES MISSING"), |
| 1611 | (colnr_T)0, TRUE); |
| 1612 | } |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1613 | ++idx; // get same block again for next index |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1614 | continue; |
| 1615 | } |
| 1616 | |
| 1617 | /* |
| 1618 | * going one block deeper in the tree |
| 1619 | */ |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1620 | if ((top = ml_add_stack(buf)) < 0) // new entry in stack |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1621 | { |
| 1622 | ++error; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1623 | break; // out of memory |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1624 | } |
| 1625 | ip = &(buf->b_ml.ml_stack[top]); |
| 1626 | ip->ip_bnum = bnum; |
| 1627 | ip->ip_index = idx; |
| 1628 | |
| 1629 | bnum = pp->pb_pointer[idx].pe_bnum; |
| 1630 | line_count = pp->pb_pointer[idx].pe_line_count; |
| 1631 | page_count = pp->pb_pointer[idx].pe_page_count; |
Bram Moolenaar | 986a003 | 2011-06-13 01:07:27 +0200 | [diff] [blame] | 1632 | idx = 0; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1633 | continue; |
| 1634 | } |
| 1635 | } |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1636 | else // not a pointer block |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1637 | { |
| 1638 | dp = (DATA_BL *)(hp->bh_data); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1639 | if (dp->db_id != DATA_ID) // block id wrong |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1640 | { |
| 1641 | if (bnum == 1) |
| 1642 | { |
Bram Moolenaar | eaaac01 | 2022-01-02 17:00:40 +0000 | [diff] [blame] | 1643 | semsg(_(e_block_one_id_wrong_str_not_swp_file), |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1644 | mfp->mf_fname); |
| 1645 | goto theend; |
| 1646 | } |
| 1647 | ++error; |
| 1648 | ml_append(lnum++, (char_u *)_("???BLOCK MISSING"), |
| 1649 | (colnr_T)0, TRUE); |
| 1650 | } |
| 1651 | else |
| 1652 | { |
| 1653 | /* |
Bram Moolenaar | bf1b713 | 2023-04-27 21:13:12 +0100 | [diff] [blame] | 1654 | * It is a data block. |
| 1655 | * Append all the lines in this block. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1656 | */ |
| 1657 | has_error = FALSE; |
Bram Moolenaar | bf1b713 | 2023-04-27 21:13:12 +0100 | [diff] [blame] | 1658 | |
| 1659 | // Check the length of the block. |
| 1660 | // If wrong, use the length given in the pointer block. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1661 | if (page_count * mfp->mf_page_size != dp->db_txt_end) |
| 1662 | { |
| 1663 | ml_append(lnum++, (char_u *)_("??? from here until ???END lines may be messed up"), |
| 1664 | (colnr_T)0, TRUE); |
| 1665 | ++error; |
| 1666 | has_error = TRUE; |
| 1667 | dp->db_txt_end = page_count * mfp->mf_page_size; |
| 1668 | } |
| 1669 | |
Bram Moolenaar | bf1b713 | 2023-04-27 21:13:12 +0100 | [diff] [blame] | 1670 | // Make sure there is a NUL at the end of the block so we |
| 1671 | // don't go over the end when copying text. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1672 | *((char_u *)dp + dp->db_txt_end - 1) = NUL; |
| 1673 | |
Bram Moolenaar | bf1b713 | 2023-04-27 21:13:12 +0100 | [diff] [blame] | 1674 | // Check the number of lines in the block. |
| 1675 | // If wrong, use the count in the data block. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1676 | if (line_count != dp->db_line_count) |
| 1677 | { |
| 1678 | ml_append(lnum++, (char_u *)_("??? from here until ???END lines may have been inserted/deleted"), |
| 1679 | (colnr_T)0, TRUE); |
| 1680 | ++error; |
| 1681 | has_error = TRUE; |
| 1682 | } |
| 1683 | |
Bram Moolenaar | bf1b713 | 2023-04-27 21:13:12 +0100 | [diff] [blame] | 1684 | int did_questions = FALSE; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1685 | for (i = 0; i < dp->db_line_count; ++i) |
| 1686 | { |
Bram Moolenaar | bf1b713 | 2023-04-27 21:13:12 +0100 | [diff] [blame] | 1687 | if ((char_u *)&(dp->db_index[i]) |
| 1688 | >= (char_u *)dp + dp->db_txt_start) |
| 1689 | { |
| 1690 | // line count must be wrong |
| 1691 | ++error; |
| 1692 | ml_append(lnum++, |
| 1693 | (char_u *)_("??? lines may be missing"), |
| 1694 | (colnr_T)0, TRUE); |
| 1695 | break; |
| 1696 | } |
| 1697 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1698 | txt_start = (dp->db_index[i] & DB_INDEX_MASK); |
Bram Moolenaar | 740885b | 2009-11-03 14:33:17 +0000 | [diff] [blame] | 1699 | if (txt_start <= (int)HEADER_SIZE |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1700 | || txt_start >= (int)dp->db_txt_end) |
| 1701 | { |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1702 | ++error; |
Bram Moolenaar | bf1b713 | 2023-04-27 21:13:12 +0100 | [diff] [blame] | 1703 | // avoid lots of lines with "???" |
| 1704 | if (did_questions) |
| 1705 | continue; |
| 1706 | did_questions = TRUE; |
| 1707 | p = (char_u *)"???"; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1708 | } |
| 1709 | else |
Bram Moolenaar | bf1b713 | 2023-04-27 21:13:12 +0100 | [diff] [blame] | 1710 | { |
| 1711 | did_questions = FALSE; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1712 | p = (char_u *)dp + txt_start; |
Bram Moolenaar | bf1b713 | 2023-04-27 21:13:12 +0100 | [diff] [blame] | 1713 | } |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1714 | ml_append(lnum++, p, (colnr_T)0, TRUE); |
| 1715 | } |
| 1716 | if (has_error) |
Bram Moolenaar | 740885b | 2009-11-03 14:33:17 +0000 | [diff] [blame] | 1717 | ml_append(lnum++, (char_u *)_("???END"), |
| 1718 | (colnr_T)0, TRUE); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1719 | } |
| 1720 | } |
| 1721 | } |
| 1722 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1723 | if (buf->b_ml.ml_stack_top == 0) // finished |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1724 | break; |
| 1725 | |
| 1726 | /* |
| 1727 | * go one block up in the tree |
| 1728 | */ |
| 1729 | ip = &(buf->b_ml.ml_stack[--(buf->b_ml.ml_stack_top)]); |
| 1730 | bnum = ip->ip_bnum; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1731 | idx = ip->ip_index + 1; // go to next index |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1732 | page_count = 1; |
| 1733 | } |
| 1734 | |
| 1735 | /* |
Bram Moolenaar | fc2d5bd | 2010-05-15 17:06:53 +0200 | [diff] [blame] | 1736 | * Compare the buffer contents with the original file. When they differ |
| 1737 | * set the 'modified' flag. |
| 1738 | * Lines 1 - lnum are the new contents. |
| 1739 | * Lines lnum + 1 to ml_line_count are the original contents. |
| 1740 | * Line ml_line_count + 1 in the dummy empty line. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1741 | */ |
Bram Moolenaar | fc2d5bd | 2010-05-15 17:06:53 +0200 | [diff] [blame] | 1742 | if (orig_file_status != OK || curbuf->b_ml.ml_line_count != lnum * 2 + 1) |
| 1743 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1744 | // Recovering an empty file results in two lines and the first line is |
| 1745 | // empty. Don't set the modified flag then. |
Bram Moolenaar | fc2d5bd | 2010-05-15 17:06:53 +0200 | [diff] [blame] | 1746 | if (!(curbuf->b_ml.ml_line_count == 2 && *ml_get(1) == NUL)) |
| 1747 | { |
Bram Moolenaar | ec28d15 | 2019-05-11 18:36:34 +0200 | [diff] [blame] | 1748 | changed_internal(); |
Bram Moolenaar | 95c526e | 2017-02-25 14:59:34 +0100 | [diff] [blame] | 1749 | ++CHANGEDTICK(curbuf); |
Bram Moolenaar | fc2d5bd | 2010-05-15 17:06:53 +0200 | [diff] [blame] | 1750 | } |
| 1751 | } |
| 1752 | else |
| 1753 | { |
| 1754 | for (idx = 1; idx <= lnum; ++idx) |
| 1755 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1756 | // Need to copy one line, fetching the other one may flush it. |
John Marriott | 02d7a6c | 2024-02-26 21:21:17 +0100 | [diff] [blame] | 1757 | p = vim_strnsave(ml_get(idx), ml_get_len(idx)); |
Bram Moolenaar | fc2d5bd | 2010-05-15 17:06:53 +0200 | [diff] [blame] | 1758 | i = STRCMP(p, ml_get(idx + lnum)); |
| 1759 | vim_free(p); |
| 1760 | if (i != 0) |
| 1761 | { |
Bram Moolenaar | ec28d15 | 2019-05-11 18:36:34 +0200 | [diff] [blame] | 1762 | changed_internal(); |
Bram Moolenaar | 95c526e | 2017-02-25 14:59:34 +0100 | [diff] [blame] | 1763 | ++CHANGEDTICK(curbuf); |
Bram Moolenaar | fc2d5bd | 2010-05-15 17:06:53 +0200 | [diff] [blame] | 1764 | break; |
| 1765 | } |
| 1766 | } |
| 1767 | } |
| 1768 | |
| 1769 | /* |
| 1770 | * Delete the lines from the original file and the dummy line from the |
| 1771 | * empty buffer. These will now be after the last line in the buffer. |
| 1772 | */ |
| 1773 | while (curbuf->b_ml.ml_line_count > lnum |
| 1774 | && !(curbuf->b_ml.ml_flags & ML_EMPTY)) |
Bram Moolenaar | ca70c07 | 2020-05-30 20:30:46 +0200 | [diff] [blame] | 1775 | ml_delete(curbuf->b_ml.ml_line_count); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1776 | curbuf->b_flags |= BF_RECOVERED; |
Bram Moolenaar | e3f50ad | 2021-06-09 12:33:40 +0200 | [diff] [blame] | 1777 | check_cursor(); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1778 | |
| 1779 | recoverymode = FALSE; |
| 1780 | if (got_int) |
Bram Moolenaar | eaaac01 | 2022-01-02 17:00:40 +0000 | [diff] [blame] | 1781 | emsg(_(e_recovery_interrupted)); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1782 | else if (error) |
| 1783 | { |
| 1784 | ++no_wait_return; |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 1785 | msg(">>>>>>>>>>>>>"); |
Bram Moolenaar | eaaac01 | 2022-01-02 17:00:40 +0000 | [diff] [blame] | 1786 | emsg(_(e_errors_detected_while_recovering_look_for_lines_starting_with_questions)); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1787 | --no_wait_return; |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 1788 | msg(_("See \":help E312\" for more information.")); |
| 1789 | msg(">>>>>>>>>>>>>"); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1790 | } |
| 1791 | else |
| 1792 | { |
Bram Moolenaar | fc2d5bd | 2010-05-15 17:06:53 +0200 | [diff] [blame] | 1793 | if (curbuf->b_changed) |
| 1794 | { |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 1795 | msg(_("Recovery completed. You should check if everything is OK.")); |
| 1796 | msg_puts(_("\n(You might want to write out this file under another name\n")); |
| 1797 | msg_puts(_("and run diff with the original file to check for changes)")); |
Bram Moolenaar | fc2d5bd | 2010-05-15 17:06:53 +0200 | [diff] [blame] | 1798 | } |
| 1799 | else |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 1800 | msg(_("Recovery completed. Buffer contents equals file contents.")); |
Bram Moolenaar | f883508 | 2020-11-09 21:04:17 +0100 | [diff] [blame] | 1801 | msg_puts(_("\nYou may want to delete the .swp file now.")); |
| 1802 | #if defined(UNIX) || defined(MSWIN) |
Bram Moolenaar | f52f060 | 2021-03-10 21:26:37 +0100 | [diff] [blame] | 1803 | if (swapfile_process_running(b0p, fname_used)) |
Bram Moolenaar | f883508 | 2020-11-09 21:04:17 +0100 | [diff] [blame] | 1804 | { |
| 1805 | // Warn there could be an active Vim on the same file, the user may |
| 1806 | // want to kill it. |
| 1807 | msg_puts(_("\nNote: process STILL RUNNING: ")); |
| 1808 | msg_outnum(char_to_long(b0p->b0_pid)); |
| 1809 | } |
| 1810 | #endif |
| 1811 | msg_puts("\n\n"); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1812 | cmdline_row = msg_row; |
| 1813 | } |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1814 | #ifdef FEAT_CRYPT |
| 1815 | if (*buf->b_p_key != NUL && STRCMP(curbuf->b_p_key, buf->b_p_key) != 0) |
| 1816 | { |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 1817 | msg_puts(_("Using crypt key from swap file for the text file.\n")); |
Bram Moolenaar | 31e5c60 | 2022-04-15 13:53:33 +0100 | [diff] [blame] | 1818 | set_option_value_give_err((char_u *)"key", 0L, buf->b_p_key, OPT_LOCAL); |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1819 | } |
| 1820 | #endif |
Bram Moolenaar | a4d158b | 2022-08-14 14:17:45 +0100 | [diff] [blame] | 1821 | redraw_curbuf_later(UPD_NOT_VALID); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1822 | |
| 1823 | theend: |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1824 | vim_free(fname_used); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1825 | recoverymode = FALSE; |
| 1826 | if (mfp != NULL) |
| 1827 | { |
| 1828 | if (hp != NULL) |
| 1829 | mf_put(mfp, hp, FALSE, FALSE); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1830 | mf_close(mfp, FALSE); // will also vim_free(mfp->mf_fname) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1831 | } |
Bram Moolenaar | df88dda | 2007-01-09 13:34:50 +0000 | [diff] [blame] | 1832 | if (buf != NULL) |
| 1833 | { |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1834 | #ifdef FEAT_CRYPT |
| 1835 | if (buf->b_p_key != curbuf->b_p_key) |
| 1836 | free_string_option(buf->b_p_key); |
Bram Moolenaar | 0ad014c | 2010-07-25 14:00:46 +0200 | [diff] [blame] | 1837 | free_string_option(buf->b_p_cm); |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1838 | #endif |
Bram Moolenaar | df88dda | 2007-01-09 13:34:50 +0000 | [diff] [blame] | 1839 | vim_free(buf->b_ml.ml_stack); |
| 1840 | vim_free(buf); |
| 1841 | } |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1842 | if (serious_error && called_from_main) |
| 1843 | ml_close(curbuf, TRUE); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1844 | else |
| 1845 | { |
| 1846 | apply_autocmds(EVENT_BUFREADPOST, NULL, curbuf->b_fname, FALSE, curbuf); |
| 1847 | apply_autocmds(EVENT_BUFWINENTER, NULL, curbuf->b_fname, FALSE, curbuf); |
| 1848 | } |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1849 | } |
| 1850 | |
| 1851 | /* |
| 1852 | * Find the names of swap files in current directory and the directory given |
| 1853 | * with the 'directory' option. |
| 1854 | * |
| 1855 | * Used to: |
| 1856 | * - list the swap files for "vim -r" |
| 1857 | * - count the number of swap files when recovering |
| 1858 | * - list the swap files when recovering |
Bram Moolenaar | c216a7a | 2022-12-05 13:50:55 +0000 | [diff] [blame] | 1859 | * - list the swap files for swapfilelist() |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1860 | * - find the name of the n'th swap file when recovering |
| 1861 | */ |
| 1862 | int |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 1863 | recover_names( |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1864 | char_u *fname, // base for swap file name |
Bram Moolenaar | c216a7a | 2022-12-05 13:50:55 +0000 | [diff] [blame] | 1865 | int do_list, // when TRUE, list the swap file names |
| 1866 | list_T *ret_list UNUSED, // when not NULL add file names to it |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1867 | int nr, // when non-zero, return nr'th swap file name |
| 1868 | char_u **fname_out) // result when "nr" > 0 |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1869 | { |
| 1870 | int num_names; |
| 1871 | char_u *(names[6]); |
| 1872 | char_u *tail; |
| 1873 | char_u *p; |
| 1874 | int num_files; |
| 1875 | int file_count = 0; |
| 1876 | char_u **files; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1877 | char_u *dirp; |
| 1878 | char_u *dir_name; |
Bram Moolenaar | 64354da | 2010-05-25 21:37:17 +0200 | [diff] [blame] | 1879 | char_u *fname_res = NULL; |
Bram Moolenaar | 9dbe475 | 2010-05-14 17:52:42 +0200 | [diff] [blame] | 1880 | #ifdef HAVE_READLINK |
| 1881 | char_u fname_buf[MAXPATHL]; |
Bram Moolenaar | 64354da | 2010-05-25 21:37:17 +0200 | [diff] [blame] | 1882 | #endif |
Bram Moolenaar | 9dbe475 | 2010-05-14 17:52:42 +0200 | [diff] [blame] | 1883 | |
Bram Moolenaar | 64354da | 2010-05-25 21:37:17 +0200 | [diff] [blame] | 1884 | if (fname != NULL) |
| 1885 | { |
| 1886 | #ifdef HAVE_READLINK |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1887 | // Expand symlink in the file name, because the swap file is created |
| 1888 | // with the actual file instead of with the symlink. |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1889 | if (resolve_symlink(fname, fname_buf) == OK) |
| 1890 | fname_res = fname_buf; |
| 1891 | else |
Bram Moolenaar | 9dbe475 | 2010-05-14 17:52:42 +0200 | [diff] [blame] | 1892 | #endif |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1893 | fname_res = fname; |
Bram Moolenaar | 64354da | 2010-05-25 21:37:17 +0200 | [diff] [blame] | 1894 | } |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1895 | |
Bram Moolenaar | c216a7a | 2022-12-05 13:50:55 +0000 | [diff] [blame] | 1896 | if (do_list) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1897 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1898 | // use msg() to start the scrolling properly |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 1899 | msg(_("Swap files found:")); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1900 | msg_putchar('\n'); |
| 1901 | } |
| 1902 | |
| 1903 | /* |
| 1904 | * Do the loop for every directory in 'directory'. |
| 1905 | * First allocate some memory to put the directory name in. |
| 1906 | */ |
Bram Moolenaar | 964b374 | 2019-05-24 18:54:09 +0200 | [diff] [blame] | 1907 | dir_name = alloc(STRLEN(p_dir) + 1); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1908 | dirp = p_dir; |
| 1909 | while (dir_name != NULL && *dirp) |
| 1910 | { |
| 1911 | /* |
| 1912 | * Isolate a directory name from *dirp and put it in dir_name (we know |
| 1913 | * it is large enough, so use 31000 for length). |
| 1914 | * Advance dirp to next directory name. |
| 1915 | */ |
| 1916 | (void)copy_option_part(&dirp, dir_name, 31000, ","); |
| 1917 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1918 | if (dir_name[0] == '.' && dir_name[1] == NUL) // check current dir |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1919 | { |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1920 | if (fname == NULL) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1921 | { |
| 1922 | #ifdef VMS |
| 1923 | names[0] = vim_strsave((char_u *)"*_sw%"); |
| 1924 | #else |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1925 | names[0] = vim_strsave((char_u *)"*.sw?"); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1926 | #endif |
Bram Moolenaar | 4f97475 | 2019-02-17 17:44:42 +0100 | [diff] [blame] | 1927 | #if defined(UNIX) || defined(MSWIN) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1928 | // For Unix names starting with a dot are special. MS-Windows |
| 1929 | // supports this too, on some file systems. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1930 | names[1] = vim_strsave((char_u *)".*.sw?"); |
| 1931 | names[2] = vim_strsave((char_u *)".sw?"); |
| 1932 | num_names = 3; |
| 1933 | #else |
| 1934 | # ifdef VMS |
| 1935 | names[1] = vim_strsave((char_u *)".*_sw%"); |
| 1936 | num_names = 2; |
| 1937 | # else |
| 1938 | num_names = 1; |
| 1939 | # endif |
| 1940 | #endif |
| 1941 | } |
| 1942 | else |
Bram Moolenaar | 9dbe475 | 2010-05-14 17:52:42 +0200 | [diff] [blame] | 1943 | num_names = recov_file_names(names, fname_res, TRUE); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1944 | } |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1945 | else // check directory dir_name |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1946 | { |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 1947 | if (fname == NULL) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1948 | { |
| 1949 | #ifdef VMS |
| 1950 | names[0] = concat_fnames(dir_name, (char_u *)"*_sw%", TRUE); |
| 1951 | #else |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1952 | names[0] = concat_fnames(dir_name, (char_u *)"*.sw?", TRUE); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1953 | #endif |
Bram Moolenaar | 4f97475 | 2019-02-17 17:44:42 +0100 | [diff] [blame] | 1954 | #if defined(UNIX) || defined(MSWIN) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1955 | // For Unix names starting with a dot are special. MS-Windows |
| 1956 | // supports this too, on some file systems. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1957 | names[1] = concat_fnames(dir_name, (char_u *)".*.sw?", TRUE); |
| 1958 | names[2] = concat_fnames(dir_name, (char_u *)".sw?", TRUE); |
| 1959 | num_names = 3; |
| 1960 | #else |
| 1961 | # ifdef VMS |
| 1962 | names[1] = concat_fnames(dir_name, (char_u *)".*_sw%", TRUE); |
| 1963 | num_names = 2; |
| 1964 | # else |
| 1965 | num_names = 1; |
| 1966 | # endif |
| 1967 | #endif |
| 1968 | } |
| 1969 | else |
| 1970 | { |
Bram Moolenaar | 4f97475 | 2019-02-17 17:44:42 +0100 | [diff] [blame] | 1971 | #if defined(UNIX) || defined(MSWIN) |
Bram Moolenaar | b113c3a | 2017-02-28 21:26:17 +0100 | [diff] [blame] | 1972 | int len = (int)STRLEN(dir_name); |
Bram Moolenaar | c525e3a | 2017-02-18 16:59:02 +0100 | [diff] [blame] | 1973 | |
| 1974 | p = dir_name + len; |
| 1975 | if (after_pathsep(dir_name, p) && len > 1 && p[-1] == p[-2]) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1976 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 1977 | // Ends with '//', Use Full path for swap name |
zeertzjq | 242667a | 2024-07-25 20:58:42 +0200 | [diff] [blame] | 1978 | tail = make_percent_swname(dir_name, p, fname_res); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1979 | } |
| 1980 | else |
| 1981 | #endif |
| 1982 | { |
Bram Moolenaar | 9dbe475 | 2010-05-14 17:52:42 +0200 | [diff] [blame] | 1983 | tail = gettail(fname_res); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1984 | tail = concat_fnames(dir_name, tail, TRUE); |
| 1985 | } |
| 1986 | if (tail == NULL) |
| 1987 | num_names = 0; |
| 1988 | else |
| 1989 | { |
| 1990 | num_names = recov_file_names(names, tail, FALSE); |
| 1991 | vim_free(tail); |
| 1992 | } |
| 1993 | } |
| 1994 | } |
| 1995 | |
Bram Moolenaar | 0d3cb73 | 2019-05-18 13:05:18 +0200 | [diff] [blame] | 1996 | // check for out-of-memory |
Bram Moolenaar | c216a7a | 2022-12-05 13:50:55 +0000 | [diff] [blame] | 1997 | for (int i = 0; i < num_names; ++i) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1998 | { |
| 1999 | if (names[i] == NULL) |
| 2000 | { |
| 2001 | for (i = 0; i < num_names; ++i) |
| 2002 | vim_free(names[i]); |
| 2003 | num_names = 0; |
| 2004 | } |
| 2005 | } |
| 2006 | if (num_names == 0) |
| 2007 | num_files = 0; |
| 2008 | else if (expand_wildcards(num_names, names, &num_files, &files, |
Bram Moolenaar | 99499b1 | 2019-05-23 21:35:48 +0200 | [diff] [blame] | 2009 | EW_NOTENV|EW_KEEPALL|EW_FILE|EW_SILENT) == FAIL) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2010 | num_files = 0; |
| 2011 | |
| 2012 | /* |
| 2013 | * When no swap file found, wildcard expansion might have failed (e.g. |
| 2014 | * not able to execute the shell). |
| 2015 | * Try finding a swap file by simply adding ".swp" to the file name. |
| 2016 | */ |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 2017 | if (*dirp == NUL && file_count + num_files == 0 && fname != NULL) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2018 | { |
Bram Moolenaar | 8767f52 | 2016-07-01 17:17:39 +0200 | [diff] [blame] | 2019 | stat_T st; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2020 | char_u *swapname; |
| 2021 | |
Bram Moolenaar | 9dbe475 | 2010-05-14 17:52:42 +0200 | [diff] [blame] | 2022 | swapname = modname(fname_res, |
Bram Moolenaar | e60acc1 | 2011-05-10 16:41:25 +0200 | [diff] [blame] | 2023 | #if defined(VMS) |
Bram Moolenaar | 9dbe475 | 2010-05-14 17:52:42 +0200 | [diff] [blame] | 2024 | (char_u *)"_swp", FALSE |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2025 | #else |
Bram Moolenaar | 9dbe475 | 2010-05-14 17:52:42 +0200 | [diff] [blame] | 2026 | (char_u *)".swp", TRUE |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2027 | #endif |
Bram Moolenaar | 9dbe475 | 2010-05-14 17:52:42 +0200 | [diff] [blame] | 2028 | ); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2029 | if (swapname != NULL) |
| 2030 | { |
Bram Moolenaar | c799fe2 | 2019-05-28 23:08:19 +0200 | [diff] [blame] | 2031 | if (mch_stat((char *)swapname, &st) != -1) // It exists! |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2032 | { |
Bram Moolenaar | c799fe2 | 2019-05-28 23:08:19 +0200 | [diff] [blame] | 2033 | files = ALLOC_ONE(char_u *); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2034 | if (files != NULL) |
| 2035 | { |
| 2036 | files[0] = swapname; |
| 2037 | swapname = NULL; |
| 2038 | num_files = 1; |
| 2039 | } |
| 2040 | } |
| 2041 | vim_free(swapname); |
| 2042 | } |
| 2043 | } |
| 2044 | |
| 2045 | /* |
Bram Moolenaar | c216a7a | 2022-12-05 13:50:55 +0000 | [diff] [blame] | 2046 | * Remove swapfile name of the current buffer, it must be ignored. |
| 2047 | * But keep it for swapfilelist(). |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2048 | */ |
| 2049 | if (curbuf->b_ml.ml_mfp != NULL |
Bram Moolenaar | c216a7a | 2022-12-05 13:50:55 +0000 | [diff] [blame] | 2050 | && (p = curbuf->b_ml.ml_mfp->mf_fname) != NULL |
| 2051 | && ret_list == NULL) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2052 | { |
Bram Moolenaar | c216a7a | 2022-12-05 13:50:55 +0000 | [diff] [blame] | 2053 | for (int i = 0; i < num_files; ++i) |
Bram Moolenaar | 99499b1 | 2019-05-23 21:35:48 +0200 | [diff] [blame] | 2054 | // Do not expand wildcards, on windows would try to expand |
| 2055 | // "%tmp%" in "%tmp%file". |
| 2056 | if (fullpathcmp(p, files[i], TRUE, FALSE) & FPC_SAME) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2057 | { |
Bram Moolenaar | 99499b1 | 2019-05-23 21:35:48 +0200 | [diff] [blame] | 2058 | // Remove the name from files[i]. Move further entries |
| 2059 | // down. When the array becomes empty free it here, since |
| 2060 | // FreeWild() won't be called below. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2061 | vim_free(files[i]); |
Bram Moolenaar | 9439cdd | 2009-04-22 13:39:36 +0000 | [diff] [blame] | 2062 | if (--num_files == 0) |
| 2063 | vim_free(files); |
| 2064 | else |
| 2065 | for ( ; i < num_files; ++i) |
| 2066 | files[i] = files[i + 1]; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2067 | } |
| 2068 | } |
Bram Moolenaar | fc1421e | 2006-04-20 22:17:20 +0000 | [diff] [blame] | 2069 | if (nr > 0) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2070 | { |
| 2071 | file_count += num_files; |
| 2072 | if (nr <= file_count) |
| 2073 | { |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 2074 | *fname_out = vim_strsave( |
| 2075 | files[nr - 1 + num_files - file_count]); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 2076 | dirp = (char_u *)""; // stop searching |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2077 | } |
| 2078 | } |
Bram Moolenaar | c216a7a | 2022-12-05 13:50:55 +0000 | [diff] [blame] | 2079 | else if (do_list) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2080 | { |
| 2081 | if (dir_name[0] == '.' && dir_name[1] == NUL) |
| 2082 | { |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 2083 | if (fname == NULL) |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 2084 | msg_puts(_(" In current directory:\n")); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2085 | else |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 2086 | msg_puts(_(" Using specified name:\n")); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2087 | } |
| 2088 | else |
| 2089 | { |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 2090 | msg_puts(_(" In directory ")); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2091 | msg_home_replace(dir_name); |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 2092 | msg_puts(":\n"); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2093 | } |
| 2094 | |
| 2095 | if (num_files) |
| 2096 | { |
Bram Moolenaar | c216a7a | 2022-12-05 13:50:55 +0000 | [diff] [blame] | 2097 | for (int i = 0; i < num_files; ++i) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2098 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 2099 | // print the swap file name |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2100 | msg_outnum((long)++file_count); |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 2101 | msg_puts(". "); |
| 2102 | msg_puts((char *)gettail(files[i])); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2103 | msg_putchar('\n'); |
| 2104 | (void)swapfile_info(files[i]); |
| 2105 | } |
| 2106 | } |
| 2107 | else |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 2108 | msg_puts(_(" -- none --\n")); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2109 | out_flush(); |
| 2110 | } |
Bram Moolenaar | c216a7a | 2022-12-05 13:50:55 +0000 | [diff] [blame] | 2111 | #ifdef FEAT_EVAL |
| 2112 | else if (ret_list != NULL) |
| 2113 | { |
| 2114 | for (int i = 0; i < num_files; ++i) |
| 2115 | { |
| 2116 | char_u *name = concat_fnames(dir_name, files[i], TRUE); |
| 2117 | if (name != NULL) |
| 2118 | { |
| 2119 | list_append_string(ret_list, name, -1); |
| 2120 | vim_free(name); |
| 2121 | } |
| 2122 | } |
| 2123 | } |
| 2124 | #endif |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2125 | else |
| 2126 | file_count += num_files; |
| 2127 | |
Bram Moolenaar | c216a7a | 2022-12-05 13:50:55 +0000 | [diff] [blame] | 2128 | for (int i = 0; i < num_names; ++i) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2129 | vim_free(names[i]); |
Bram Moolenaar | fc1421e | 2006-04-20 22:17:20 +0000 | [diff] [blame] | 2130 | if (num_files > 0) |
| 2131 | FreeWild(num_files, files); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2132 | } |
| 2133 | vim_free(dir_name); |
| 2134 | return file_count; |
| 2135 | } |
| 2136 | |
Bram Moolenaar | 4f97475 | 2019-02-17 17:44:42 +0100 | [diff] [blame] | 2137 | #if defined(UNIX) || defined(MSWIN) || defined(PROTO) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2138 | /* |
Bram Moolenaar | b782ba4 | 2018-08-07 21:39:28 +0200 | [diff] [blame] | 2139 | * Need _very_ long file names. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2140 | * Append the full path to name with path separators made into percent |
Bram Moolenaar | 8b0e62c | 2021-10-19 22:12:25 +0100 | [diff] [blame] | 2141 | * signs, to "dir". An unnamed buffer is handled as "" (<currentdir>/"") |
| 2142 | * The last character in "dir" must be an extra slash or backslash, it is |
| 2143 | * removed. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2144 | */ |
Bram Moolenaar | b782ba4 | 2018-08-07 21:39:28 +0200 | [diff] [blame] | 2145 | char_u * |
zeertzjq | 242667a | 2024-07-25 20:58:42 +0200 | [diff] [blame] | 2146 | make_percent_swname(char_u *dir, char_u *dir_end, char_u *name) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2147 | { |
Bram Moolenaar | b782ba4 | 2018-08-07 21:39:28 +0200 | [diff] [blame] | 2148 | char_u *d = NULL, *s, *f; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2149 | |
Bram Moolenaar | b782ba4 | 2018-08-07 21:39:28 +0200 | [diff] [blame] | 2150 | f = fix_fname(name != NULL ? name : (char_u *)""); |
Yegappan Lakshmanan | e857598 | 2023-01-14 12:32:28 +0000 | [diff] [blame] | 2151 | if (f == NULL) |
| 2152 | return NULL; |
Bram Moolenaar | 8b0e62c | 2021-10-19 22:12:25 +0100 | [diff] [blame] | 2153 | |
Yegappan Lakshmanan | e857598 | 2023-01-14 12:32:28 +0000 | [diff] [blame] | 2154 | s = alloc(STRLEN(f) + 1); |
| 2155 | if (s != NULL) |
| 2156 | { |
| 2157 | STRCPY(s, f); |
| 2158 | for (d = s; *d != NUL; MB_PTR_ADV(d)) |
| 2159 | if (vim_ispathsep(*d)) |
| 2160 | *d = '%'; |
| 2161 | |
zeertzjq | 242667a | 2024-07-25 20:58:42 +0200 | [diff] [blame] | 2162 | dir_end[-1] = NUL; // remove one trailing slash |
Yegappan Lakshmanan | e857598 | 2023-01-14 12:32:28 +0000 | [diff] [blame] | 2163 | d = concat_fnames(dir, s, TRUE); |
| 2164 | vim_free(s); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2165 | } |
Yegappan Lakshmanan | e857598 | 2023-01-14 12:32:28 +0000 | [diff] [blame] | 2166 | vim_free(f); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2167 | return d; |
| 2168 | } |
| 2169 | #endif |
| 2170 | |
Bram Moolenaar | 1b243ea | 2019-04-28 22:50:40 +0200 | [diff] [blame] | 2171 | #if (defined(UNIX) || defined(VMS) || defined(MSWIN)) \ |
| 2172 | && (defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)) |
| 2173 | # define HAVE_PROCESS_STILL_RUNNING |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2174 | static int process_still_running; |
| 2175 | #endif |
| 2176 | |
Bram Moolenaar | 47ad565 | 2018-08-21 21:09:07 +0200 | [diff] [blame] | 2177 | #if defined(FEAT_EVAL) || defined(PROTO) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2178 | /* |
Bram Moolenaar | 00f123a | 2018-08-21 20:28:54 +0200 | [diff] [blame] | 2179 | * Return information found in swapfile "fname" in dictionary "d". |
| 2180 | * This is used by the swapinfo() function. |
| 2181 | */ |
| 2182 | void |
| 2183 | get_b0_dict(char_u *fname, dict_T *d) |
| 2184 | { |
| 2185 | int fd; |
| 2186 | struct block0 b0; |
| 2187 | |
| 2188 | if ((fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0)) >= 0) |
| 2189 | { |
| 2190 | if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0)) |
| 2191 | { |
Bram Moolenaar | 47ad565 | 2018-08-21 21:09:07 +0200 | [diff] [blame] | 2192 | if (ml_check_b0_id(&b0) == FAIL) |
Bram Moolenaar | e6fdf79 | 2018-12-26 22:57:42 +0100 | [diff] [blame] | 2193 | dict_add_string(d, "error", (char_u *)"Not a swap file"); |
Bram Moolenaar | 47ad565 | 2018-08-21 21:09:07 +0200 | [diff] [blame] | 2194 | else if (b0_magic_wrong(&b0)) |
Bram Moolenaar | e6fdf79 | 2018-12-26 22:57:42 +0100 | [diff] [blame] | 2195 | dict_add_string(d, "error", (char_u *)"Magic number mismatch"); |
Bram Moolenaar | 00f123a | 2018-08-21 20:28:54 +0200 | [diff] [blame] | 2196 | else |
| 2197 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 2198 | // we have swap information |
Bram Moolenaar | e6fdf79 | 2018-12-26 22:57:42 +0100 | [diff] [blame] | 2199 | dict_add_string_len(d, "version", b0.b0_version, 10); |
| 2200 | dict_add_string_len(d, "user", b0.b0_uname, B0_UNAME_SIZE); |
| 2201 | dict_add_string_len(d, "host", b0.b0_hname, B0_HNAME_SIZE); |
| 2202 | dict_add_string_len(d, "fname", b0.b0_fname, B0_FNAME_SIZE_ORG); |
Bram Moolenaar | 00f123a | 2018-08-21 20:28:54 +0200 | [diff] [blame] | 2203 | |
| 2204 | dict_add_number(d, "pid", char_to_long(b0.b0_pid)); |
| 2205 | dict_add_number(d, "mtime", char_to_long(b0.b0_mtime)); |
Bram Moolenaar | 47ad565 | 2018-08-21 21:09:07 +0200 | [diff] [blame] | 2206 | dict_add_number(d, "dirty", b0.b0_dirty ? 1 : 0); |
| 2207 | # ifdef CHECK_INODE |
Bram Moolenaar | 00f123a | 2018-08-21 20:28:54 +0200 | [diff] [blame] | 2208 | dict_add_number(d, "inode", char_to_long(b0.b0_ino)); |
Bram Moolenaar | 47ad565 | 2018-08-21 21:09:07 +0200 | [diff] [blame] | 2209 | # endif |
Bram Moolenaar | 00f123a | 2018-08-21 20:28:54 +0200 | [diff] [blame] | 2210 | } |
| 2211 | } |
| 2212 | else |
Bram Moolenaar | e6fdf79 | 2018-12-26 22:57:42 +0100 | [diff] [blame] | 2213 | dict_add_string(d, "error", (char_u *)"Cannot read file"); |
Bram Moolenaar | 00f123a | 2018-08-21 20:28:54 +0200 | [diff] [blame] | 2214 | close(fd); |
| 2215 | } |
| 2216 | else |
Bram Moolenaar | e6fdf79 | 2018-12-26 22:57:42 +0100 | [diff] [blame] | 2217 | dict_add_string(d, "error", (char_u *)"Cannot open file"); |
Bram Moolenaar | 00f123a | 2018-08-21 20:28:54 +0200 | [diff] [blame] | 2218 | } |
Bram Moolenaar | 47ad565 | 2018-08-21 21:09:07 +0200 | [diff] [blame] | 2219 | #endif |
Bram Moolenaar | 00f123a | 2018-08-21 20:28:54 +0200 | [diff] [blame] | 2220 | |
| 2221 | /* |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 2222 | * Give information about an existing swap file. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2223 | * Returns timestamp (0 when unknown). |
| 2224 | */ |
| 2225 | static time_t |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 2226 | swapfile_info(char_u *fname) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2227 | { |
Bram Moolenaar | 8767f52 | 2016-07-01 17:17:39 +0200 | [diff] [blame] | 2228 | stat_T st; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2229 | int fd; |
| 2230 | struct block0 b0; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2231 | #ifdef UNIX |
| 2232 | char_u uname[B0_UNAME_SIZE]; |
| 2233 | #endif |
| 2234 | |
Bram Moolenaar | 63d2555 | 2019-05-10 21:28:38 +0200 | [diff] [blame] | 2235 | // print the swap file date |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2236 | if (mch_stat((char *)fname, &st) != -1) |
| 2237 | { |
| 2238 | #ifdef UNIX |
Bram Moolenaar | 63d2555 | 2019-05-10 21:28:38 +0200 | [diff] [blame] | 2239 | // print name of owner of the file |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2240 | if (mch_get_uname(st.st_uid, uname, B0_UNAME_SIZE) == OK) |
| 2241 | { |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 2242 | msg_puts(_(" owned by: ")); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2243 | msg_outtrans(uname); |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 2244 | msg_puts(_(" dated: ")); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2245 | } |
| 2246 | else |
| 2247 | #endif |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 2248 | msg_puts(_(" dated: ")); |
Bram Moolenaar | 63d2555 | 2019-05-10 21:28:38 +0200 | [diff] [blame] | 2249 | msg_puts(get_ctime(st.st_mtime, TRUE)); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2250 | } |
Bram Moolenaar | 63d2555 | 2019-05-10 21:28:38 +0200 | [diff] [blame] | 2251 | else |
| 2252 | st.st_mtime = 0; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2253 | |
| 2254 | /* |
| 2255 | * print the original file name |
| 2256 | */ |
| 2257 | fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0); |
| 2258 | if (fd >= 0) |
| 2259 | { |
Bram Moolenaar | 540fc6f | 2010-12-17 16:27:16 +0100 | [diff] [blame] | 2260 | if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0)) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2261 | { |
| 2262 | if (STRNCMP(b0.b0_version, "VIM 3.0", 7) == 0) |
| 2263 | { |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 2264 | msg_puts(_(" [from Vim version 3.0]")); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2265 | } |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 2266 | else if (ml_check_b0_id(&b0) == FAIL) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2267 | { |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 2268 | msg_puts(_(" [does not look like a Vim swap file]")); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2269 | } |
| 2270 | else |
| 2271 | { |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 2272 | msg_puts(_(" file name: ")); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2273 | if (b0.b0_fname[0] == NUL) |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 2274 | msg_puts(_("[No Name]")); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2275 | else |
| 2276 | msg_outtrans(b0.b0_fname); |
| 2277 | |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 2278 | msg_puts(_("\n modified: ")); |
| 2279 | msg_puts(b0.b0_dirty ? _("YES") : _("no")); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2280 | |
| 2281 | if (*(b0.b0_uname) != NUL) |
| 2282 | { |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 2283 | msg_puts(_("\n user name: ")); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2284 | msg_outtrans(b0.b0_uname); |
| 2285 | } |
| 2286 | |
| 2287 | if (*(b0.b0_hname) != NUL) |
| 2288 | { |
| 2289 | if (*(b0.b0_uname) != NUL) |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 2290 | msg_puts(_(" host name: ")); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2291 | else |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 2292 | msg_puts(_("\n host name: ")); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2293 | msg_outtrans(b0.b0_hname); |
| 2294 | } |
| 2295 | |
| 2296 | if (char_to_long(b0.b0_pid) != 0L) |
| 2297 | { |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 2298 | msg_puts(_("\n process ID: ")); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2299 | msg_outnum(char_to_long(b0.b0_pid)); |
Bram Moolenaar | 67cf86b | 2019-04-28 22:25:38 +0200 | [diff] [blame] | 2300 | #if defined(UNIX) || defined(MSWIN) |
Bram Moolenaar | f52f060 | 2021-03-10 21:26:37 +0100 | [diff] [blame] | 2301 | if (swapfile_process_running(&b0, fname)) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2302 | { |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 2303 | msg_puts(_(" (STILL RUNNING)")); |
Bram Moolenaar | 1b243ea | 2019-04-28 22:50:40 +0200 | [diff] [blame] | 2304 | # ifdef HAVE_PROCESS_STILL_RUNNING |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2305 | process_still_running = TRUE; |
| 2306 | # endif |
| 2307 | } |
| 2308 | #endif |
| 2309 | } |
| 2310 | |
| 2311 | if (b0_magic_wrong(&b0)) |
| 2312 | { |
Bram Moolenaar | 48e330a | 2016-02-23 14:53:34 +0100 | [diff] [blame] | 2313 | #if defined(MSWIN) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2314 | if (STRNCMP(b0.b0_hname, "PC ", 3) == 0) |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 2315 | msg_puts(_("\n [not usable with this version of Vim]")); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2316 | else |
| 2317 | #endif |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 2318 | msg_puts(_("\n [not usable on this computer]")); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2319 | } |
| 2320 | } |
| 2321 | } |
| 2322 | else |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 2323 | msg_puts(_(" [cannot be read]")); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2324 | close(fd); |
| 2325 | } |
| 2326 | else |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 2327 | msg_puts(_(" [cannot be opened]")); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2328 | msg_putchar('\n'); |
| 2329 | |
Bram Moolenaar | 63d2555 | 2019-05-10 21:28:38 +0200 | [diff] [blame] | 2330 | return st.st_mtime; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2331 | } |
| 2332 | |
Bram Moolenaar | 67cf86b | 2019-04-28 22:25:38 +0200 | [diff] [blame] | 2333 | /* |
| 2334 | * Return TRUE if the swap file looks OK and there are no changes, thus it can |
| 2335 | * be safely deleted. |
| 2336 | */ |
zeertzjq | 3c5999e | 2022-03-23 13:54:51 +0000 | [diff] [blame] | 2337 | static int |
Bram Moolenaar | 67cf86b | 2019-04-28 22:25:38 +0200 | [diff] [blame] | 2338 | swapfile_unchanged(char_u *fname) |
| 2339 | { |
| 2340 | stat_T st; |
| 2341 | int fd; |
| 2342 | struct block0 b0; |
| 2343 | int ret = TRUE; |
Bram Moolenaar | 67cf86b | 2019-04-28 22:25:38 +0200 | [diff] [blame] | 2344 | |
| 2345 | // must be able to stat the swap file |
| 2346 | if (mch_stat((char *)fname, &st) == -1) |
| 2347 | return FALSE; |
| 2348 | |
| 2349 | // must be able to read the first block |
| 2350 | fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0); |
| 2351 | if (fd < 0) |
| 2352 | return FALSE; |
| 2353 | if (read_eintr(fd, &b0, sizeof(b0)) != sizeof(b0)) |
| 2354 | { |
| 2355 | close(fd); |
| 2356 | return FALSE; |
| 2357 | } |
| 2358 | |
| 2359 | // the ID and magic number must be correct |
| 2360 | if (ml_check_b0_id(&b0) == FAIL|| b0_magic_wrong(&b0)) |
| 2361 | ret = FALSE; |
| 2362 | |
| 2363 | // must be unchanged |
| 2364 | if (b0.b0_dirty) |
| 2365 | ret = FALSE; |
| 2366 | |
| 2367 | #if defined(UNIX) || defined(MSWIN) |
Bram Moolenaar | f883508 | 2020-11-09 21:04:17 +0100 | [diff] [blame] | 2368 | // Host name must be known and must equal the current host name, otherwise |
| 2369 | // comparing pid is meaningless. |
| 2370 | if (*(b0.b0_hname) == NUL) |
| 2371 | { |
| 2372 | ret = FALSE; |
| 2373 | } |
| 2374 | else |
| 2375 | { |
| 2376 | char_u hostname[B0_HNAME_SIZE]; |
| 2377 | |
| 2378 | mch_get_host_name(hostname, B0_HNAME_SIZE); |
| 2379 | hostname[B0_HNAME_SIZE - 1] = NUL; |
Bram Moolenaar | e79cdb6 | 2020-11-21 13:51:16 +0100 | [diff] [blame] | 2380 | b0.b0_hname[B0_HNAME_SIZE - 1] = NUL; // in case of corruption |
Bram Moolenaar | f883508 | 2020-11-09 21:04:17 +0100 | [diff] [blame] | 2381 | if (STRICMP(b0.b0_hname, hostname) != 0) |
| 2382 | ret = FALSE; |
| 2383 | } |
| 2384 | |
Bram Moolenaar | 32aa102 | 2019-11-02 22:54:41 +0100 | [diff] [blame] | 2385 | // process must be known and not be running |
Bram Moolenaar | f52f060 | 2021-03-10 21:26:37 +0100 | [diff] [blame] | 2386 | if (char_to_long(b0.b0_pid) == 0L || swapfile_process_running(&b0, fname)) |
Bram Moolenaar | 67cf86b | 2019-04-28 22:25:38 +0200 | [diff] [blame] | 2387 | ret = FALSE; |
| 2388 | #endif |
| 2389 | |
Bram Moolenaar | f883508 | 2020-11-09 21:04:17 +0100 | [diff] [blame] | 2390 | // We do not check the user, it should be irrelevant for whether the swap |
| 2391 | // file is still useful. |
Bram Moolenaar | 67cf86b | 2019-04-28 22:25:38 +0200 | [diff] [blame] | 2392 | |
| 2393 | close(fd); |
| 2394 | return ret; |
| 2395 | } |
| 2396 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2397 | static int |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 2398 | recov_file_names(char_u **names, char_u *path, int prepend_dot) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2399 | { |
| 2400 | int num_names; |
| 2401 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2402 | /* |
| 2403 | * (Win32 and Win64) never short names, but do prepend a dot. |
| 2404 | * (Not MS-DOS or Win32 or Win64) maybe short name, maybe not: Try both. |
| 2405 | * Only use the short name if it is different. |
| 2406 | */ |
| 2407 | char_u *p; |
| 2408 | int i; |
Bram Moolenaar | 4f97475 | 2019-02-17 17:44:42 +0100 | [diff] [blame] | 2409 | # ifndef MSWIN |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2410 | int shortname = curbuf->b_shortname; |
| 2411 | |
| 2412 | curbuf->b_shortname = FALSE; |
| 2413 | # endif |
| 2414 | |
| 2415 | num_names = 0; |
| 2416 | |
| 2417 | /* |
| 2418 | * May also add the file name with a dot prepended, for swap file in same |
| 2419 | * dir as original file. |
| 2420 | */ |
| 2421 | if (prepend_dot) |
| 2422 | { |
| 2423 | names[num_names] = modname(path, (char_u *)".sw?", TRUE); |
| 2424 | if (names[num_names] == NULL) |
| 2425 | goto end; |
| 2426 | ++num_names; |
| 2427 | } |
| 2428 | |
| 2429 | /* |
| 2430 | * Form the normal swap file name pattern by appending ".sw?". |
| 2431 | */ |
| 2432 | #ifdef VMS |
| 2433 | names[num_names] = concat_fnames(path, (char_u *)"_sw%", FALSE); |
| 2434 | #else |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2435 | names[num_names] = concat_fnames(path, (char_u *)".sw?", FALSE); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2436 | #endif |
| 2437 | if (names[num_names] == NULL) |
| 2438 | goto end; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 2439 | if (num_names >= 1) // check if we have the same name twice |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2440 | { |
| 2441 | p = names[num_names - 1]; |
| 2442 | i = (int)STRLEN(names[num_names - 1]) - (int)STRLEN(names[num_names]); |
| 2443 | if (i > 0) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 2444 | p += i; // file name has been expanded to full path |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2445 | |
| 2446 | if (STRCMP(p, names[num_names]) != 0) |
| 2447 | ++num_names; |
| 2448 | else |
| 2449 | vim_free(names[num_names]); |
| 2450 | } |
| 2451 | else |
| 2452 | ++num_names; |
| 2453 | |
Bram Moolenaar | 4f97475 | 2019-02-17 17:44:42 +0100 | [diff] [blame] | 2454 | # ifndef MSWIN |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2455 | /* |
| 2456 | * Also try with 'shortname' set, in case the file is on a DOS filesystem. |
| 2457 | */ |
| 2458 | curbuf->b_shortname = TRUE; |
| 2459 | #ifdef VMS |
| 2460 | names[num_names] = modname(path, (char_u *)"_sw%", FALSE); |
| 2461 | #else |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2462 | names[num_names] = modname(path, (char_u *)".sw?", FALSE); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2463 | #endif |
| 2464 | if (names[num_names] == NULL) |
| 2465 | goto end; |
| 2466 | |
| 2467 | /* |
| 2468 | * Remove the one from 'shortname', if it's the same as with 'noshortname'. |
| 2469 | */ |
| 2470 | p = names[num_names]; |
| 2471 | i = STRLEN(names[num_names]) - STRLEN(names[num_names - 1]); |
| 2472 | if (i > 0) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 2473 | p += i; // file name has been expanded to full path |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2474 | if (STRCMP(names[num_names - 1], p) == 0) |
| 2475 | vim_free(names[num_names]); |
| 2476 | else |
| 2477 | ++num_names; |
| 2478 | # endif |
| 2479 | |
| 2480 | end: |
Bram Moolenaar | 4f97475 | 2019-02-17 17:44:42 +0100 | [diff] [blame] | 2481 | # ifndef MSWIN |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2482 | curbuf->b_shortname = shortname; |
| 2483 | # endif |
| 2484 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2485 | return num_names; |
| 2486 | } |
| 2487 | |
| 2488 | /* |
| 2489 | * sync all memlines |
| 2490 | * |
| 2491 | * If 'check_file' is TRUE, check if original file exists and was not changed. |
| 2492 | * If 'check_char' is TRUE, stop syncing when character becomes available, but |
| 2493 | * always sync at least one block. |
| 2494 | */ |
| 2495 | void |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 2496 | ml_sync_all(int check_file, int check_char) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2497 | { |
| 2498 | buf_T *buf; |
Bram Moolenaar | 8767f52 | 2016-07-01 17:17:39 +0200 | [diff] [blame] | 2499 | stat_T st; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2500 | |
Bram Moolenaar | 2932359 | 2016-07-24 22:04:11 +0200 | [diff] [blame] | 2501 | FOR_ALL_BUFFERS(buf) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2502 | { |
Christian Brabandt | f573c6e | 2021-06-20 14:02:16 +0200 | [diff] [blame] | 2503 | if (buf->b_ml.ml_mfp == NULL |
| 2504 | || buf->b_ml.ml_mfp->mf_fname == NULL |
| 2505 | || buf->b_ml.ml_mfp->mf_fd < 0) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 2506 | continue; // no file |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2507 | |
Christian Brabandt | 19e6c4f | 2023-06-27 18:57:10 +0100 | [diff] [blame] | 2508 | #ifdef FEAT_CRYPT |
| 2509 | if (crypt_may_close_swapfile(buf, buf->b_p_key, |
| 2510 | crypt_get_method_nr(buf))) |
| 2511 | continue; |
| 2512 | #endif |
| 2513 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 2514 | ml_flush_line(buf); // flush buffered line |
| 2515 | // flush locked block |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2516 | (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); |
| 2517 | if (bufIsChanged(buf) && check_file && mf_need_trans(buf->b_ml.ml_mfp) |
| 2518 | && buf->b_ffname != NULL) |
| 2519 | { |
| 2520 | /* |
| 2521 | * If the original file does not exist anymore or has been changed |
| 2522 | * call ml_preserve() to get rid of all negative numbered blocks. |
| 2523 | */ |
| 2524 | if (mch_stat((char *)buf->b_ffname, &st) == -1 |
| 2525 | || st.st_mtime != buf->b_mtime_read |
Leah Neukirchen | 0a7984a | 2021-10-14 21:27:55 +0100 | [diff] [blame] | 2526 | #ifdef ST_MTIM_NSEC |
| 2527 | || st.ST_MTIM_NSEC != buf->b_mtime_read_ns |
| 2528 | #endif |
Bram Moolenaar | 914703b | 2010-05-31 21:59:46 +0200 | [diff] [blame] | 2529 | || st.st_size != buf->b_orig_size) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2530 | { |
| 2531 | ml_preserve(buf, FALSE); |
| 2532 | did_check_timestamps = FALSE; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 2533 | need_check_timestamps = TRUE; // give message later |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2534 | } |
| 2535 | } |
Bram Moolenaar | 3a2a60c | 2023-05-27 18:02:55 +0100 | [diff] [blame] | 2536 | if (buf->b_ml.ml_mfp->mf_dirty == MF_DIRTY_YES) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2537 | { |
| 2538 | (void)mf_sync(buf->b_ml.ml_mfp, (check_char ? MFS_STOP : 0) |
| 2539 | | (bufIsChanged(buf) ? MFS_FLUSH : 0)); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 2540 | if (check_char && ui_char_avail()) // character available now |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2541 | break; |
| 2542 | } |
| 2543 | } |
| 2544 | } |
| 2545 | |
| 2546 | /* |
| 2547 | * sync one buffer, including negative blocks |
| 2548 | * |
| 2549 | * after this all the blocks are in the swap file |
| 2550 | * |
| 2551 | * Used for the :preserve command and when the original file has been |
| 2552 | * changed or deleted. |
| 2553 | * |
| 2554 | * when message is TRUE the success of preserving is reported |
| 2555 | */ |
| 2556 | void |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 2557 | ml_preserve(buf_T *buf, int message) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2558 | { |
| 2559 | bhdr_T *hp; |
| 2560 | linenr_T lnum; |
| 2561 | memfile_T *mfp = buf->b_ml.ml_mfp; |
| 2562 | int status; |
| 2563 | int got_int_save = got_int; |
| 2564 | |
| 2565 | if (mfp == NULL || mfp->mf_fname == NULL) |
| 2566 | { |
| 2567 | if (message) |
Bram Moolenaar | eaaac01 | 2022-01-02 17:00:40 +0000 | [diff] [blame] | 2568 | emsg(_(e_cannot_preserve_there_is_no_swap_file)); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2569 | return; |
| 2570 | } |
Christian Brabandt | 19e6c4f | 2023-06-27 18:57:10 +0100 | [diff] [blame] | 2571 | #ifdef FEAT_CRYPT |
| 2572 | if (crypt_may_close_swapfile(buf, buf->b_p_key, crypt_get_method_nr(buf))) |
| 2573 | return; |
| 2574 | #endif |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2575 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 2576 | // We only want to stop when interrupted here, not when interrupted |
| 2577 | // before. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2578 | got_int = FALSE; |
| 2579 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 2580 | ml_flush_line(buf); // flush buffered line |
| 2581 | (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush locked block |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2582 | status = mf_sync(mfp, MFS_ALL | MFS_FLUSH); |
| 2583 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 2584 | // stack is invalid after mf_sync(.., MFS_ALL) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2585 | buf->b_ml.ml_stack_top = 0; |
| 2586 | |
| 2587 | /* |
| 2588 | * Some of the data blocks may have been changed from negative to |
| 2589 | * positive block number. In that case the pointer blocks need to be |
| 2590 | * updated. |
| 2591 | * |
| 2592 | * We don't know in which pointer block the references are, so we visit |
| 2593 | * all data blocks until there are no more translations to be done (or |
| 2594 | * we hit the end of the file, which can only happen in case a write fails, |
| 2595 | * e.g. when file system if full). |
| 2596 | * ml_find_line() does the work by translating the negative block numbers |
| 2597 | * when getting the first line of each data block. |
| 2598 | */ |
| 2599 | if (mf_need_trans(mfp) && !got_int) |
| 2600 | { |
| 2601 | lnum = 1; |
| 2602 | while (mf_need_trans(mfp) && lnum <= buf->b_ml.ml_line_count) |
| 2603 | { |
| 2604 | hp = ml_find_line(buf, lnum, ML_FIND); |
| 2605 | if (hp == NULL) |
| 2606 | { |
| 2607 | status = FAIL; |
| 2608 | goto theend; |
| 2609 | } |
| 2610 | CHECK(buf->b_ml.ml_locked_low != lnum, "low != lnum"); |
| 2611 | lnum = buf->b_ml.ml_locked_high + 1; |
| 2612 | } |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 2613 | (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush locked block |
| 2614 | // sync the updated pointer blocks |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2615 | if (mf_sync(mfp, MFS_ALL | MFS_FLUSH) == FAIL) |
| 2616 | status = FAIL; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 2617 | buf->b_ml.ml_stack_top = 0; // stack is invalid now |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2618 | } |
| 2619 | theend: |
| 2620 | got_int |= got_int_save; |
| 2621 | |
| 2622 | if (message) |
| 2623 | { |
| 2624 | if (status == OK) |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 2625 | msg(_("File preserved")); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2626 | else |
Bram Moolenaar | eaaac01 | 2022-01-02 17:00:40 +0000 | [diff] [blame] | 2627 | emsg(_(e_preserve_failed)); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2628 | } |
| 2629 | } |
| 2630 | |
| 2631 | /* |
| 2632 | * NOTE: The pointer returned by the ml_get_*() functions only remains valid |
| 2633 | * until the next call! |
| 2634 | * line1 = ml_get(1); |
| 2635 | * line2 = ml_get(2); // line1 is now invalid! |
| 2636 | * Make a copy of the line if necessary. |
| 2637 | */ |
| 2638 | /* |
Bram Moolenaar | 2e2e13c | 2010-12-08 13:17:03 +0100 | [diff] [blame] | 2639 | * Return a pointer to a (read-only copy of a) line. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2640 | * |
| 2641 | * On failure an error message is given and IObuff is returned (to avoid |
| 2642 | * having to check for error everywhere). |
| 2643 | */ |
| 2644 | char_u * |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 2645 | ml_get(linenr_T lnum) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2646 | { |
| 2647 | return ml_get_buf(curbuf, lnum, FALSE); |
| 2648 | } |
| 2649 | |
| 2650 | /* |
Bram Moolenaar | 2e2e13c | 2010-12-08 13:17:03 +0100 | [diff] [blame] | 2651 | * Return pointer to position "pos". |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2652 | */ |
| 2653 | char_u * |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 2654 | ml_get_pos(pos_T *pos) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2655 | { |
| 2656 | return (ml_get_buf(curbuf, pos->lnum, FALSE) + pos->col); |
| 2657 | } |
| 2658 | |
| 2659 | /* |
Bram Moolenaar | 2e2e13c | 2010-12-08 13:17:03 +0100 | [diff] [blame] | 2660 | * Return pointer to cursor line. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2661 | */ |
| 2662 | char_u * |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 2663 | ml_get_curline(void) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2664 | { |
| 2665 | return ml_get_buf(curbuf, curwin->w_cursor.lnum, FALSE); |
| 2666 | } |
| 2667 | |
| 2668 | /* |
Bram Moolenaar | 2e2e13c | 2010-12-08 13:17:03 +0100 | [diff] [blame] | 2669 | * Return pointer to cursor position. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2670 | */ |
| 2671 | char_u * |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 2672 | ml_get_cursor(void) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2673 | { |
| 2674 | return (ml_get_buf(curbuf, curwin->w_cursor.lnum, FALSE) + |
| 2675 | curwin->w_cursor.col); |
| 2676 | } |
| 2677 | |
John Marriott | 02d7a6c | 2024-02-26 21:21:17 +0100 | [diff] [blame] | 2678 | // return length (excluding the NUL) of the given line |
| 2679 | colnr_T |
| 2680 | ml_get_len(linenr_T lnum) |
| 2681 | { |
| 2682 | return ml_get_buf_len(curbuf, lnum); |
| 2683 | } |
| 2684 | |
zeertzjq | 94b7c32 | 2024-03-12 21:50:32 +0100 | [diff] [blame] | 2685 | // return length (excluding the NUL) of the text after position "pos" |
| 2686 | colnr_T |
| 2687 | ml_get_pos_len(pos_T *pos) |
| 2688 | { |
zeertzjq | 8c55d60 | 2024-03-13 20:42:26 +0100 | [diff] [blame] | 2689 | return ml_get_buf_len(curbuf, pos->lnum) - pos->col; |
zeertzjq | 94b7c32 | 2024-03-12 21:50:32 +0100 | [diff] [blame] | 2690 | } |
| 2691 | |
John Marriott | 02d7a6c | 2024-02-26 21:21:17 +0100 | [diff] [blame] | 2692 | // return length (excluding the NUL) of the cursor line |
| 2693 | colnr_T |
| 2694 | ml_get_curline_len(void) |
| 2695 | { |
| 2696 | return ml_get_buf_len(curbuf, curwin->w_cursor.lnum); |
| 2697 | } |
| 2698 | |
| 2699 | // return length (excluding the NUL) of the cursor position |
| 2700 | colnr_T |
| 2701 | ml_get_cursor_len(void) |
| 2702 | { |
| 2703 | return ml_get_buf_len(curbuf, curwin->w_cursor.lnum) - curwin->w_cursor.col; |
| 2704 | } |
| 2705 | |
| 2706 | // return length (excluding the NUL) of the given line in the given buffer |
| 2707 | colnr_T |
| 2708 | ml_get_buf_len(buf_T *buf, linenr_T lnum) |
| 2709 | { |
zeertzjq | 82e079d | 2024-03-10 08:55:42 +0100 | [diff] [blame] | 2710 | char_u *line; |
| 2711 | |
| 2712 | if (*(line = ml_get_buf(buf, lnum, FALSE)) == NUL) |
zeertzjq | d9be94c | 2024-07-14 10:20:20 +0200 | [diff] [blame] | 2713 | return 0; |
John Marriott | 02d7a6c | 2024-02-26 21:21:17 +0100 | [diff] [blame] | 2714 | |
zeertzjq | 82e079d | 2024-03-10 08:55:42 +0100 | [diff] [blame] | 2715 | if (buf->b_ml.ml_line_textlen <= 0) |
| 2716 | buf->b_ml.ml_line_textlen = (int)STRLEN(line) + 1; |
Christian Brabandt | a72d1be | 2024-03-05 20:43:25 +0100 | [diff] [blame] | 2717 | return buf->b_ml.ml_line_textlen - 1; |
John Marriott | 02d7a6c | 2024-02-26 21:21:17 +0100 | [diff] [blame] | 2718 | } |
| 2719 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2720 | /* |
Bram Moolenaar | 2e2e13c | 2010-12-08 13:17:03 +0100 | [diff] [blame] | 2721 | * Return a pointer to a line in a specific buffer |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2722 | * |
| 2723 | * "will_change": if TRUE mark the buffer dirty (chars in the line will be |
| 2724 | * changed) |
| 2725 | */ |
| 2726 | char_u * |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 2727 | ml_get_buf( |
| 2728 | buf_T *buf, |
| 2729 | linenr_T lnum, |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 2730 | int will_change) // line will be changed |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2731 | { |
Bram Moolenaar | ad40f02 | 2007-02-13 03:01:39 +0000 | [diff] [blame] | 2732 | bhdr_T *hp; |
| 2733 | DATA_BL *dp; |
Bram Moolenaar | ad40f02 | 2007-02-13 03:01:39 +0000 | [diff] [blame] | 2734 | static int recursive = 0; |
Bram Moolenaar | 96e7a59 | 2021-11-25 13:52:37 +0000 | [diff] [blame] | 2735 | static char_u questions[4]; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2736 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 2737 | if (lnum > buf->b_ml.ml_line_count) // invalid line number |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2738 | { |
Bram Moolenaar | ad40f02 | 2007-02-13 03:01:39 +0000 | [diff] [blame] | 2739 | if (recursive == 0) |
| 2740 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 2741 | // Avoid giving this message for a recursive call, may happen when |
| 2742 | // the GUI redraws part of the text. |
Bram Moolenaar | ad40f02 | 2007-02-13 03:01:39 +0000 | [diff] [blame] | 2743 | ++recursive; |
RestorerZ | 68ebcee | 2023-05-31 17:12:14 +0100 | [diff] [blame] | 2744 | siemsg(e_ml_get_invalid_lnum_nr, lnum); |
Bram Moolenaar | ad40f02 | 2007-02-13 03:01:39 +0000 | [diff] [blame] | 2745 | --recursive; |
| 2746 | } |
Bram Moolenaar | f9435e4 | 2022-02-16 16:33:28 +0000 | [diff] [blame] | 2747 | ml_flush_line(buf); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2748 | errorret: |
Bram Moolenaar | 96e7a59 | 2021-11-25 13:52:37 +0000 | [diff] [blame] | 2749 | STRCPY(questions, "???"); |
Bram Moolenaar | adfde11 | 2019-05-25 22:11:45 +0200 | [diff] [blame] | 2750 | buf->b_ml.ml_line_len = 4; |
Christian Brabandt | a72d1be | 2024-03-05 20:43:25 +0100 | [diff] [blame] | 2751 | buf->b_ml.ml_line_textlen = buf->b_ml.ml_line_len; |
Bram Moolenaar | f9435e4 | 2022-02-16 16:33:28 +0000 | [diff] [blame] | 2752 | buf->b_ml.ml_line_lnum = lnum; |
Bram Moolenaar | 96e7a59 | 2021-11-25 13:52:37 +0000 | [diff] [blame] | 2753 | return questions; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2754 | } |
Bram Moolenaar | adfde11 | 2019-05-25 22:11:45 +0200 | [diff] [blame] | 2755 | if (lnum <= 0) // pretend line 0 is line 1 |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2756 | lnum = 1; |
| 2757 | |
Bram Moolenaar | adfde11 | 2019-05-25 22:11:45 +0200 | [diff] [blame] | 2758 | if (buf->b_ml.ml_mfp == NULL) // there are no lines |
| 2759 | { |
| 2760 | buf->b_ml.ml_line_len = 1; |
Christian Brabandt | a72d1be | 2024-03-05 20:43:25 +0100 | [diff] [blame] | 2761 | buf->b_ml.ml_line_textlen = buf->b_ml.ml_line_len; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2762 | return (char_u *)""; |
Bram Moolenaar | adfde11 | 2019-05-25 22:11:45 +0200 | [diff] [blame] | 2763 | } |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2764 | |
Bram Moolenaar | 37d619f | 2010-03-10 14:46:26 +0100 | [diff] [blame] | 2765 | /* |
| 2766 | * See if it is the same line as requested last time. |
| 2767 | * Otherwise may need to flush last used line. |
| 2768 | * Don't use the last used line when 'swapfile' is reset, need to load all |
| 2769 | * blocks. |
| 2770 | */ |
Bram Moolenaar | 47b8b15 | 2007-02-07 02:41:57 +0000 | [diff] [blame] | 2771 | if (buf->b_ml.ml_line_lnum != lnum || mf_dont_release) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2772 | { |
Bram Moolenaar | 98aefe7 | 2018-12-13 22:20:09 +0100 | [diff] [blame] | 2773 | unsigned start, end; |
Bram Moolenaar | 98aefe7 | 2018-12-13 22:20:09 +0100 | [diff] [blame] | 2774 | int idx; |
| 2775 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2776 | ml_flush_line(buf); |
| 2777 | |
| 2778 | /* |
| 2779 | * Find the data block containing the line. |
| 2780 | * This also fills the stack with the blocks from the root to the data |
| 2781 | * block and releases any locked block. |
| 2782 | */ |
| 2783 | if ((hp = ml_find_line(buf, lnum, ML_FIND)) == NULL) |
| 2784 | { |
Bram Moolenaar | ad40f02 | 2007-02-13 03:01:39 +0000 | [diff] [blame] | 2785 | if (recursive == 0) |
| 2786 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 2787 | // Avoid giving this message for a recursive call, may happen |
| 2788 | // when the GUI redraws part of the text. |
Bram Moolenaar | ad40f02 | 2007-02-13 03:01:39 +0000 | [diff] [blame] | 2789 | ++recursive; |
Bram Moolenaar | cb86893 | 2019-10-26 20:56:21 +0200 | [diff] [blame] | 2790 | get_trans_bufname(buf); |
| 2791 | shorten_dir(NameBuff); |
RestorerZ | 68ebcee | 2023-05-31 17:12:14 +0100 | [diff] [blame] | 2792 | siemsg(e_ml_get_cannot_find_line_nr_in_buffer_nr_str, |
Bram Moolenaar | cb86893 | 2019-10-26 20:56:21 +0200 | [diff] [blame] | 2793 | lnum, buf->b_fnum, NameBuff); |
Bram Moolenaar | ad40f02 | 2007-02-13 03:01:39 +0000 | [diff] [blame] | 2794 | --recursive; |
| 2795 | } |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2796 | goto errorret; |
| 2797 | } |
| 2798 | |
| 2799 | dp = (DATA_BL *)(hp->bh_data); |
| 2800 | |
Bram Moolenaar | 98aefe7 | 2018-12-13 22:20:09 +0100 | [diff] [blame] | 2801 | idx = lnum - buf->b_ml.ml_locked_low; |
| 2802 | start = ((dp->db_index[idx]) & DB_INDEX_MASK); |
| 2803 | // The text ends where the previous line starts. The first line ends |
| 2804 | // at the end of the block. |
| 2805 | if (idx == 0) |
| 2806 | end = dp->db_txt_end; |
| 2807 | else |
| 2808 | end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK); |
Bram Moolenaar | 98aefe7 | 2018-12-13 22:20:09 +0100 | [diff] [blame] | 2809 | |
| 2810 | buf->b_ml.ml_line_ptr = (char_u *)dp + start; |
John Marriott | 02d7a6c | 2024-02-26 21:21:17 +0100 | [diff] [blame] | 2811 | buf->b_ml.ml_line_len = end - start; |
Christian Brabandt | a72d1be | 2024-03-05 20:43:25 +0100 | [diff] [blame] | 2812 | #if defined(FEAT_BYTEOFF) && defined(FEAT_PROP_POPUP) |
zeertzjq | 82e079d | 2024-03-10 08:55:42 +0100 | [diff] [blame] | 2813 | // Text properties come after a NUL byte, so ml_line_len should be |
| 2814 | // larger than the size of textprop_T if there is any. |
| 2815 | if (buf->b_has_textprop |
| 2816 | && (size_t)buf->b_ml.ml_line_len > sizeof(textprop_T)) |
| 2817 | buf->b_ml.ml_line_textlen = 0; // call STRLEN() later when needed |
Christian Brabandt | a72d1be | 2024-03-05 20:43:25 +0100 | [diff] [blame] | 2818 | else |
| 2819 | #endif |
| 2820 | buf->b_ml.ml_line_textlen = buf->b_ml.ml_line_len; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2821 | buf->b_ml.ml_line_lnum = lnum; |
Bram Moolenaar | fa4873c | 2022-06-30 22:13:59 +0100 | [diff] [blame] | 2822 | buf->b_ml.ml_flags &= ~(ML_LINE_DIRTY | ML_ALLOCATED); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2823 | } |
| 2824 | if (will_change) |
Bram Moolenaar | fa4873c | 2022-06-30 22:13:59 +0100 | [diff] [blame] | 2825 | { |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2826 | buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS); |
Bram Moolenaar | fa4873c | 2022-06-30 22:13:59 +0100 | [diff] [blame] | 2827 | #ifdef FEAT_EVAL |
| 2828 | if (ml_get_alloc_lines && (buf->b_ml.ml_flags & ML_ALLOCATED)) |
| 2829 | // can't make the change in the data block |
| 2830 | buf->b_ml.ml_flags |= ML_LINE_DIRTY; |
| 2831 | #endif |
| 2832 | } |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2833 | |
Bram Moolenaar | fa4873c | 2022-06-30 22:13:59 +0100 | [diff] [blame] | 2834 | #ifdef FEAT_EVAL |
| 2835 | if (ml_get_alloc_lines |
| 2836 | && (buf->b_ml.ml_flags & (ML_LINE_DIRTY | ML_ALLOCATED)) == 0) |
| 2837 | { |
| 2838 | char_u *p = alloc(buf->b_ml.ml_line_len); |
| 2839 | |
| 2840 | // make sure the text is in allocated memory |
| 2841 | if (p != NULL) |
| 2842 | { |
| 2843 | memmove(p, buf->b_ml.ml_line_ptr, buf->b_ml.ml_line_len); |
| 2844 | buf->b_ml.ml_line_ptr = p; |
| 2845 | buf->b_ml.ml_flags |= ML_ALLOCATED; |
| 2846 | if (will_change) |
| 2847 | // can't make the change in the data block |
| 2848 | buf->b_ml.ml_flags |= ML_LINE_DIRTY; |
| 2849 | } |
| 2850 | } |
| 2851 | #endif |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2852 | return buf->b_ml.ml_line_ptr; |
| 2853 | } |
| 2854 | |
| 2855 | /* |
| 2856 | * Check if a line that was just obtained by a call to ml_get |
| 2857 | * is in allocated memory. |
Bram Moolenaar | fa4873c | 2022-06-30 22:13:59 +0100 | [diff] [blame] | 2858 | * This ignores ML_ALLOCATED to get the same behavior as without the test |
| 2859 | * override. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2860 | */ |
| 2861 | int |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 2862 | ml_line_alloced(void) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2863 | { |
| 2864 | return (curbuf->b_ml.ml_flags & ML_LINE_DIRTY); |
| 2865 | } |
| 2866 | |
Bram Moolenaar | 05ad5ff | 2019-11-30 22:48:27 +0100 | [diff] [blame] | 2867 | #ifdef FEAT_PROP_POPUP |
Bram Moolenaar | a9d4b84 | 2020-05-30 14:46:52 +0200 | [diff] [blame] | 2868 | /* |
| 2869 | * Add text properties that continue from the previous line. |
| 2870 | */ |
Bram Moolenaar | b56ac04 | 2018-12-28 23:22:40 +0100 | [diff] [blame] | 2871 | static void |
| 2872 | add_text_props_for_append( |
| 2873 | buf_T *buf, |
| 2874 | linenr_T lnum, |
| 2875 | char_u **line, |
| 2876 | int *len, |
| 2877 | char_u **tofree) |
| 2878 | { |
| 2879 | int round; |
| 2880 | int new_prop_count = 0; |
| 2881 | int count; |
| 2882 | int n; |
| 2883 | char_u *props; |
Bram Moolenaar | ea78145 | 2019-09-04 18:53:12 +0200 | [diff] [blame] | 2884 | int new_len = 0; // init for gcc |
Bram Moolenaar | 8f13d82 | 2020-09-12 21:04:23 +0200 | [diff] [blame] | 2885 | char_u *new_line = NULL; |
Bram Moolenaar | b56ac04 | 2018-12-28 23:22:40 +0100 | [diff] [blame] | 2886 | textprop_T prop; |
| 2887 | |
| 2888 | // Make two rounds: |
| 2889 | // 1. calculate the extra space needed |
| 2890 | // 2. allocate the space and fill it |
| 2891 | for (round = 1; round <= 2; ++round) |
| 2892 | { |
| 2893 | if (round == 2) |
| 2894 | { |
| 2895 | if (new_prop_count == 0) |
| 2896 | return; // nothing to do |
| 2897 | new_len = *len + new_prop_count * sizeof(textprop_T); |
Bram Moolenaar | 964b374 | 2019-05-24 18:54:09 +0200 | [diff] [blame] | 2898 | new_line = alloc(new_len); |
Bram Moolenaar | b56ac04 | 2018-12-28 23:22:40 +0100 | [diff] [blame] | 2899 | if (new_line == NULL) |
| 2900 | return; |
| 2901 | mch_memmove(new_line, *line, *len); |
| 2902 | new_prop_count = 0; |
| 2903 | } |
| 2904 | |
| 2905 | // Get the line above to find any props that continue in the next |
| 2906 | // line. |
| 2907 | count = get_text_props(buf, lnum, &props, FALSE); |
| 2908 | for (n = 0; n < count; ++n) |
| 2909 | { |
Bram Moolenaar | a9d4b84 | 2020-05-30 14:46:52 +0200 | [diff] [blame] | 2910 | mch_memmove(&prop, props + n * sizeof(textprop_T), |
| 2911 | sizeof(textprop_T)); |
Bram Moolenaar | b56ac04 | 2018-12-28 23:22:40 +0100 | [diff] [blame] | 2912 | if (prop.tp_flags & TP_FLAG_CONT_NEXT) |
| 2913 | { |
| 2914 | if (round == 2) |
| 2915 | { |
| 2916 | prop.tp_flags |= TP_FLAG_CONT_PREV; |
| 2917 | prop.tp_col = 1; |
Bram Moolenaar | a9d4b84 | 2020-05-30 14:46:52 +0200 | [diff] [blame] | 2918 | prop.tp_len = *len; // not exactly the right length |
| 2919 | mch_memmove(new_line + *len + new_prop_count |
| 2920 | * sizeof(textprop_T), &prop, sizeof(textprop_T)); |
Bram Moolenaar | b56ac04 | 2018-12-28 23:22:40 +0100 | [diff] [blame] | 2921 | } |
| 2922 | ++new_prop_count; |
| 2923 | } |
| 2924 | } |
| 2925 | } |
| 2926 | *line = new_line; |
| 2927 | *tofree = new_line; |
| 2928 | *len = new_len; |
| 2929 | } |
| 2930 | #endif |
| 2931 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2932 | static int |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 2933 | ml_append_int( |
| 2934 | buf_T *buf, |
Bram Moolenaar | 98aefe7 | 2018-12-13 22:20:09 +0100 | [diff] [blame] | 2935 | linenr_T lnum, // append after this line (can be 0) |
Bram Moolenaar | b56ac04 | 2018-12-28 23:22:40 +0100 | [diff] [blame] | 2936 | char_u *line_arg, // text of the new line |
Bram Moolenaar | 98aefe7 | 2018-12-13 22:20:09 +0100 | [diff] [blame] | 2937 | colnr_T len_arg, // length of line, including NUL, or 0 |
Bram Moolenaar | a9d4b84 | 2020-05-30 14:46:52 +0200 | [diff] [blame] | 2938 | int flags) // ML_APPEND_ flags |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2939 | { |
Bram Moolenaar | b56ac04 | 2018-12-28 23:22:40 +0100 | [diff] [blame] | 2940 | char_u *line = line_arg; |
| 2941 | colnr_T len = len_arg; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2942 | int i; |
Bram Moolenaar | 98aefe7 | 2018-12-13 22:20:09 +0100 | [diff] [blame] | 2943 | int line_count; // number of indexes in current block |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2944 | int offset; |
| 2945 | int from, to; |
Bram Moolenaar | 98aefe7 | 2018-12-13 22:20:09 +0100 | [diff] [blame] | 2946 | int space_needed; // space needed for new line |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2947 | int page_size; |
| 2948 | int page_count; |
Bram Moolenaar | 98aefe7 | 2018-12-13 22:20:09 +0100 | [diff] [blame] | 2949 | int db_idx; // index for lnum in data block |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2950 | bhdr_T *hp; |
| 2951 | memfile_T *mfp; |
| 2952 | DATA_BL *dp; |
| 2953 | PTR_BL *pp; |
| 2954 | infoptr_T *ip; |
Bram Moolenaar | 05ad5ff | 2019-11-30 22:48:27 +0100 | [diff] [blame] | 2955 | #ifdef FEAT_PROP_POPUP |
Bram Moolenaar | b56ac04 | 2018-12-28 23:22:40 +0100 | [diff] [blame] | 2956 | char_u *tofree = NULL; |
Bram Moolenaar | 50652b0 | 2022-08-07 21:48:37 +0100 | [diff] [blame] | 2957 | # ifdef FEAT_BYTEOFF |
| 2958 | colnr_T text_len = 0; // text len with NUL without text properties |
| 2959 | # endif |
Bram Moolenaar | b56ac04 | 2018-12-28 23:22:40 +0100 | [diff] [blame] | 2960 | #endif |
| 2961 | int ret = FAIL; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2962 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2963 | if (lnum > buf->b_ml.ml_line_count || buf->b_ml.ml_mfp == NULL) |
Bram Moolenaar | b56ac04 | 2018-12-28 23:22:40 +0100 | [diff] [blame] | 2964 | return FAIL; // lnum out of range |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2965 | |
| 2966 | if (lowest_marked && lowest_marked > lnum) |
| 2967 | lowest_marked = lnum + 1; |
| 2968 | |
| 2969 | if (len == 0) |
Bram Moolenaar | 50652b0 | 2022-08-07 21:48:37 +0100 | [diff] [blame] | 2970 | { |
Bram Moolenaar | 98aefe7 | 2018-12-13 22:20:09 +0100 | [diff] [blame] | 2971 | len = (colnr_T)STRLEN(line) + 1; // space needed for the text |
Bram Moolenaar | 50652b0 | 2022-08-07 21:48:37 +0100 | [diff] [blame] | 2972 | #if defined(FEAT_PROP_POPUP) && defined(FEAT_BYTEOFF) |
| 2973 | text_len = len; |
| 2974 | #endif |
| 2975 | } |
| 2976 | #if defined(FEAT_PROP_POPUP) && defined(FEAT_BYTEOFF) |
| 2977 | else if (curbuf->b_has_textprop) |
| 2978 | // "len" may include text properties, get the length of the text. |
| 2979 | text_len = (colnr_T)STRLEN(line) + 1; |
| 2980 | else |
| 2981 | text_len = len; |
| 2982 | #endif |
Bram Moolenaar | b56ac04 | 2018-12-28 23:22:40 +0100 | [diff] [blame] | 2983 | |
Bram Moolenaar | 05ad5ff | 2019-11-30 22:48:27 +0100 | [diff] [blame] | 2984 | #ifdef FEAT_PROP_POPUP |
Bram Moolenaar | 840f91f | 2021-05-26 22:32:10 +0200 | [diff] [blame] | 2985 | if (curbuf->b_has_textprop && lnum > 0 |
| 2986 | && !(flags & (ML_APPEND_UNDO | ML_APPEND_NOPROP))) |
Bram Moolenaar | b56ac04 | 2018-12-28 23:22:40 +0100 | [diff] [blame] | 2987 | // Add text properties that continue from the previous line. |
| 2988 | add_text_props_for_append(buf, lnum, &line, &len, &tofree); |
| 2989 | #endif |
| 2990 | |
Bram Moolenaar | 98aefe7 | 2018-12-13 22:20:09 +0100 | [diff] [blame] | 2991 | space_needed = len + INDEX_SIZE; // space needed for text + index |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 2992 | |
| 2993 | mfp = buf->b_ml.ml_mfp; |
| 2994 | page_size = mfp->mf_page_size; |
| 2995 | |
| 2996 | /* |
| 2997 | * find the data block containing the previous line |
| 2998 | * This also fills the stack with the blocks from the root to the data block |
| 2999 | * This also releases any locked block. |
| 3000 | */ |
| 3001 | if ((hp = ml_find_line(buf, lnum == 0 ? (linenr_T)1 : lnum, |
| 3002 | ML_INSERT)) == NULL) |
Bram Moolenaar | b56ac04 | 2018-12-28 23:22:40 +0100 | [diff] [blame] | 3003 | goto theend; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3004 | |
| 3005 | buf->b_ml.ml_flags &= ~ML_EMPTY; |
| 3006 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3007 | if (lnum == 0) // got line one instead, correct db_idx |
| 3008 | db_idx = -1; // careful, it is negative! |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3009 | else |
| 3010 | db_idx = lnum - buf->b_ml.ml_locked_low; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3011 | // get line count before the insertion |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3012 | line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low; |
| 3013 | |
| 3014 | dp = (DATA_BL *)(hp->bh_data); |
| 3015 | |
| 3016 | /* |
| 3017 | * If |
| 3018 | * - there is not enough room in the current block |
| 3019 | * - appending to the last line in the block |
| 3020 | * - not appending to the last line in the file |
| 3021 | * insert in front of the next block. |
| 3022 | */ |
| 3023 | if ((int)dp->db_free < space_needed && db_idx == line_count - 1 |
| 3024 | && lnum < buf->b_ml.ml_line_count) |
| 3025 | { |
| 3026 | /* |
| 3027 | * Now that the line is not going to be inserted in the block that we |
| 3028 | * expected, the line count has to be adjusted in the pointer blocks |
| 3029 | * by using ml_locked_lineadd. |
| 3030 | */ |
| 3031 | --(buf->b_ml.ml_locked_lineadd); |
| 3032 | --(buf->b_ml.ml_locked_high); |
| 3033 | if ((hp = ml_find_line(buf, lnum + 1, ML_INSERT)) == NULL) |
Bram Moolenaar | b56ac04 | 2018-12-28 23:22:40 +0100 | [diff] [blame] | 3034 | goto theend; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3035 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3036 | db_idx = -1; // careful, it is negative! |
| 3037 | // get line count before the insertion |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3038 | line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low; |
| 3039 | CHECK(buf->b_ml.ml_locked_low != lnum + 1, "locked_low != lnum + 1"); |
| 3040 | |
| 3041 | dp = (DATA_BL *)(hp->bh_data); |
| 3042 | } |
| 3043 | |
| 3044 | ++buf->b_ml.ml_line_count; |
| 3045 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3046 | if ((int)dp->db_free >= space_needed) // enough room in data block |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3047 | { |
Bram Moolenaar | b56ac04 | 2018-12-28 23:22:40 +0100 | [diff] [blame] | 3048 | /* |
| 3049 | * Insert the new line in an existing data block, or in the data block |
| 3050 | * allocated above. |
| 3051 | */ |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3052 | dp->db_txt_start -= len; |
| 3053 | dp->db_free -= space_needed; |
| 3054 | ++(dp->db_line_count); |
| 3055 | |
| 3056 | /* |
| 3057 | * move the text of the lines that follow to the front |
| 3058 | * adjust the indexes of the lines that follow |
| 3059 | */ |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3060 | if (line_count > db_idx + 1) // if there are following lines |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3061 | { |
| 3062 | /* |
| 3063 | * Offset is the start of the previous line. |
| 3064 | * This will become the character just after the new line. |
| 3065 | */ |
| 3066 | if (db_idx < 0) |
| 3067 | offset = dp->db_txt_end; |
| 3068 | else |
| 3069 | offset = ((dp->db_index[db_idx]) & DB_INDEX_MASK); |
| 3070 | mch_memmove((char *)dp + dp->db_txt_start, |
| 3071 | (char *)dp + dp->db_txt_start + len, |
| 3072 | (size_t)(offset - (dp->db_txt_start + len))); |
| 3073 | for (i = line_count - 1; i > db_idx; --i) |
| 3074 | dp->db_index[i + 1] = dp->db_index[i] - len; |
| 3075 | dp->db_index[db_idx + 1] = offset - len; |
| 3076 | } |
Bram Moolenaar | 98aefe7 | 2018-12-13 22:20:09 +0100 | [diff] [blame] | 3077 | else |
| 3078 | // add line at the end (which is the start of the text) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3079 | dp->db_index[db_idx + 1] = dp->db_txt_start; |
| 3080 | |
| 3081 | /* |
| 3082 | * copy the text into the block |
| 3083 | */ |
| 3084 | mch_memmove((char *)dp + dp->db_index[db_idx + 1], line, (size_t)len); |
Bram Moolenaar | a9d4b84 | 2020-05-30 14:46:52 +0200 | [diff] [blame] | 3085 | if (flags & ML_APPEND_MARK) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3086 | dp->db_index[db_idx + 1] |= DB_MARKED; |
| 3087 | |
| 3088 | /* |
| 3089 | * Mark the block dirty. |
| 3090 | */ |
| 3091 | buf->b_ml.ml_flags |= ML_LOCKED_DIRTY; |
Bram Moolenaar | a9d4b84 | 2020-05-30 14:46:52 +0200 | [diff] [blame] | 3092 | if (!(flags & ML_APPEND_NEW)) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3093 | buf->b_ml.ml_flags |= ML_LOCKED_POS; |
| 3094 | } |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3095 | else // not enough space in data block |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3096 | { |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3097 | long line_count_left, line_count_right; |
| 3098 | int page_count_left, page_count_right; |
| 3099 | bhdr_T *hp_left; |
| 3100 | bhdr_T *hp_right; |
| 3101 | bhdr_T *hp_new; |
| 3102 | int lines_moved; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3103 | int data_moved = 0; // init to shut up gcc |
| 3104 | int total_moved = 0; // init to shut up gcc |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3105 | DATA_BL *dp_right, *dp_left; |
| 3106 | int stack_idx; |
| 3107 | int in_left; |
| 3108 | int lineadd; |
| 3109 | blocknr_T bnum_left, bnum_right; |
| 3110 | linenr_T lnum_left, lnum_right; |
| 3111 | int pb_idx; |
| 3112 | PTR_BL *pp_new; |
| 3113 | |
| 3114 | /* |
Bram Moolenaar | b56ac04 | 2018-12-28 23:22:40 +0100 | [diff] [blame] | 3115 | * There is not enough room, we have to create a new data block and |
| 3116 | * copy some lines into it. |
| 3117 | * Then we have to insert an entry in the pointer block. |
| 3118 | * If this pointer block also is full, we go up another block, and so |
| 3119 | * on, up to the root if necessary. |
| 3120 | * The line counts in the pointer blocks have already been adjusted by |
| 3121 | * ml_find_line(). |
| 3122 | * |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3123 | * We are going to allocate a new data block. Depending on the |
| 3124 | * situation it will be put to the left or right of the existing |
| 3125 | * block. If possible we put the new line in the left block and move |
| 3126 | * the lines after it to the right block. Otherwise the new line is |
| 3127 | * also put in the right block. This method is more efficient when |
| 3128 | * inserting a lot of lines at one place. |
| 3129 | */ |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3130 | if (db_idx < 0) // left block is new, right block is existing |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3131 | { |
| 3132 | lines_moved = 0; |
| 3133 | in_left = TRUE; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3134 | // space_needed does not change |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3135 | } |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3136 | else // left block is existing, right block is new |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3137 | { |
| 3138 | lines_moved = line_count - db_idx - 1; |
| 3139 | if (lines_moved == 0) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3140 | in_left = FALSE; // put new line in right block |
| 3141 | // space_needed does not change |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3142 | else |
| 3143 | { |
| 3144 | data_moved = ((dp->db_index[db_idx]) & DB_INDEX_MASK) - |
| 3145 | dp->db_txt_start; |
| 3146 | total_moved = data_moved + lines_moved * INDEX_SIZE; |
| 3147 | if ((int)dp->db_free + total_moved >= space_needed) |
| 3148 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3149 | in_left = TRUE; // put new line in left block |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3150 | space_needed = total_moved; |
| 3151 | } |
| 3152 | else |
| 3153 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3154 | in_left = FALSE; // put new line in right block |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3155 | space_needed += total_moved; |
| 3156 | } |
| 3157 | } |
| 3158 | } |
| 3159 | |
| 3160 | page_count = ((space_needed + HEADER_SIZE) + page_size - 1) / page_size; |
Bram Moolenaar | a9d4b84 | 2020-05-30 14:46:52 +0200 | [diff] [blame] | 3161 | if ((hp_new = ml_new_data(mfp, flags & ML_APPEND_NEW, page_count)) |
| 3162 | == NULL) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3163 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3164 | // correct line counts in pointer blocks |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3165 | --(buf->b_ml.ml_locked_lineadd); |
| 3166 | --(buf->b_ml.ml_locked_high); |
Bram Moolenaar | b56ac04 | 2018-12-28 23:22:40 +0100 | [diff] [blame] | 3167 | goto theend; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3168 | } |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3169 | if (db_idx < 0) // left block is new |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3170 | { |
| 3171 | hp_left = hp_new; |
| 3172 | hp_right = hp; |
| 3173 | line_count_left = 0; |
| 3174 | line_count_right = line_count; |
| 3175 | } |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3176 | else // right block is new |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3177 | { |
| 3178 | hp_left = hp; |
| 3179 | hp_right = hp_new; |
| 3180 | line_count_left = line_count; |
| 3181 | line_count_right = 0; |
| 3182 | } |
| 3183 | dp_right = (DATA_BL *)(hp_right->bh_data); |
| 3184 | dp_left = (DATA_BL *)(hp_left->bh_data); |
| 3185 | bnum_left = hp_left->bh_bnum; |
| 3186 | bnum_right = hp_right->bh_bnum; |
| 3187 | page_count_left = hp_left->bh_page_count; |
| 3188 | page_count_right = hp_right->bh_page_count; |
| 3189 | |
| 3190 | /* |
| 3191 | * May move the new line into the right/new block. |
| 3192 | */ |
| 3193 | if (!in_left) |
| 3194 | { |
| 3195 | dp_right->db_txt_start -= len; |
| 3196 | dp_right->db_free -= len + INDEX_SIZE; |
| 3197 | dp_right->db_index[0] = dp_right->db_txt_start; |
Bram Moolenaar | a9d4b84 | 2020-05-30 14:46:52 +0200 | [diff] [blame] | 3198 | if (flags & ML_APPEND_MARK) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3199 | dp_right->db_index[0] |= DB_MARKED; |
| 3200 | |
| 3201 | mch_memmove((char *)dp_right + dp_right->db_txt_start, |
| 3202 | line, (size_t)len); |
| 3203 | ++line_count_right; |
| 3204 | } |
| 3205 | /* |
| 3206 | * may move lines from the left/old block to the right/new one. |
| 3207 | */ |
| 3208 | if (lines_moved) |
| 3209 | { |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3210 | dp_right->db_txt_start -= data_moved; |
| 3211 | dp_right->db_free -= total_moved; |
| 3212 | mch_memmove((char *)dp_right + dp_right->db_txt_start, |
| 3213 | (char *)dp_left + dp_left->db_txt_start, |
| 3214 | (size_t)data_moved); |
| 3215 | offset = dp_right->db_txt_start - dp_left->db_txt_start; |
| 3216 | dp_left->db_txt_start += data_moved; |
| 3217 | dp_left->db_free += total_moved; |
| 3218 | |
| 3219 | /* |
| 3220 | * update indexes in the new block |
| 3221 | */ |
| 3222 | for (to = line_count_right, from = db_idx + 1; |
| 3223 | from < line_count_left; ++from, ++to) |
| 3224 | dp_right->db_index[to] = dp->db_index[from] + offset; |
| 3225 | line_count_right += lines_moved; |
| 3226 | line_count_left -= lines_moved; |
| 3227 | } |
| 3228 | |
| 3229 | /* |
| 3230 | * May move the new line into the left (old or new) block. |
| 3231 | */ |
| 3232 | if (in_left) |
| 3233 | { |
| 3234 | dp_left->db_txt_start -= len; |
| 3235 | dp_left->db_free -= len + INDEX_SIZE; |
| 3236 | dp_left->db_index[line_count_left] = dp_left->db_txt_start; |
Bram Moolenaar | a9d4b84 | 2020-05-30 14:46:52 +0200 | [diff] [blame] | 3237 | if (flags & ML_APPEND_MARK) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3238 | dp_left->db_index[line_count_left] |= DB_MARKED; |
| 3239 | mch_memmove((char *)dp_left + dp_left->db_txt_start, |
| 3240 | line, (size_t)len); |
| 3241 | ++line_count_left; |
| 3242 | } |
| 3243 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3244 | if (db_idx < 0) // left block is new |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3245 | { |
| 3246 | lnum_left = lnum + 1; |
| 3247 | lnum_right = 0; |
| 3248 | } |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3249 | else // right block is new |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3250 | { |
| 3251 | lnum_left = 0; |
| 3252 | if (in_left) |
| 3253 | lnum_right = lnum + 2; |
| 3254 | else |
| 3255 | lnum_right = lnum + 1; |
| 3256 | } |
| 3257 | dp_left->db_line_count = line_count_left; |
| 3258 | dp_right->db_line_count = line_count_right; |
| 3259 | |
| 3260 | /* |
| 3261 | * release the two data blocks |
| 3262 | * The new one (hp_new) already has a correct blocknumber. |
| 3263 | * The old one (hp, in ml_locked) gets a positive blocknumber if |
| 3264 | * we changed it and we are not editing a new file. |
| 3265 | */ |
| 3266 | if (lines_moved || in_left) |
| 3267 | buf->b_ml.ml_flags |= ML_LOCKED_DIRTY; |
Bram Moolenaar | a9d4b84 | 2020-05-30 14:46:52 +0200 | [diff] [blame] | 3268 | if (!(flags & ML_APPEND_NEW) && db_idx >= 0 && in_left) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3269 | buf->b_ml.ml_flags |= ML_LOCKED_POS; |
| 3270 | mf_put(mfp, hp_new, TRUE, FALSE); |
| 3271 | |
| 3272 | /* |
| 3273 | * flush the old data block |
| 3274 | * set ml_locked_lineadd to 0, because the updating of the |
| 3275 | * pointer blocks is done below |
| 3276 | */ |
| 3277 | lineadd = buf->b_ml.ml_locked_lineadd; |
| 3278 | buf->b_ml.ml_locked_lineadd = 0; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3279 | ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush data block |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3280 | |
| 3281 | /* |
| 3282 | * update pointer blocks for the new data block |
| 3283 | */ |
| 3284 | for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0; |
| 3285 | --stack_idx) |
| 3286 | { |
| 3287 | ip = &(buf->b_ml.ml_stack[stack_idx]); |
| 3288 | pb_idx = ip->ip_index; |
| 3289 | if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL) |
Bram Moolenaar | b56ac04 | 2018-12-28 23:22:40 +0100 | [diff] [blame] | 3290 | goto theend; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3291 | pp = (PTR_BL *)(hp->bh_data); // must be pointer block |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3292 | if (pp->pb_id != PTR_ID) |
| 3293 | { |
RestorerZ | 68ebcee | 2023-05-31 17:12:14 +0100 | [diff] [blame] | 3294 | iemsg(e_pointer_block_id_wrong_three); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3295 | mf_put(mfp, hp, FALSE, FALSE); |
Bram Moolenaar | b56ac04 | 2018-12-28 23:22:40 +0100 | [diff] [blame] | 3296 | goto theend; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3297 | } |
| 3298 | /* |
| 3299 | * TODO: If the pointer block is full and we are adding at the end |
| 3300 | * try to insert in front of the next block |
| 3301 | */ |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3302 | // block not full, add one entry |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3303 | if (pp->pb_count < pp->pb_count_max) |
| 3304 | { |
| 3305 | if (pb_idx + 1 < (int)pp->pb_count) |
| 3306 | mch_memmove(&pp->pb_pointer[pb_idx + 2], |
| 3307 | &pp->pb_pointer[pb_idx + 1], |
| 3308 | (size_t)(pp->pb_count - pb_idx - 1) * sizeof(PTR_EN)); |
| 3309 | ++pp->pb_count; |
| 3310 | pp->pb_pointer[pb_idx].pe_line_count = line_count_left; |
| 3311 | pp->pb_pointer[pb_idx].pe_bnum = bnum_left; |
| 3312 | pp->pb_pointer[pb_idx].pe_page_count = page_count_left; |
| 3313 | pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right; |
| 3314 | pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right; |
| 3315 | pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right; |
| 3316 | |
| 3317 | if (lnum_left != 0) |
| 3318 | pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left; |
| 3319 | if (lnum_right != 0) |
| 3320 | pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right; |
| 3321 | |
| 3322 | mf_put(mfp, hp, TRUE, FALSE); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3323 | buf->b_ml.ml_stack_top = stack_idx + 1; // truncate stack |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3324 | |
| 3325 | if (lineadd) |
| 3326 | { |
| 3327 | --(buf->b_ml.ml_stack_top); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3328 | // fix line count for rest of blocks in the stack |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3329 | ml_lineadd(buf, lineadd); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3330 | // fix stack itself |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3331 | buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high += |
| 3332 | lineadd; |
| 3333 | ++(buf->b_ml.ml_stack_top); |
| 3334 | } |
| 3335 | |
| 3336 | /* |
| 3337 | * We are finished, break the loop here. |
| 3338 | */ |
| 3339 | break; |
| 3340 | } |
=?UTF-8?q?Dundar=20G=C3=B6c?= | f26c161 | 2022-04-07 13:26:34 +0100 | [diff] [blame] | 3341 | // pointer block full |
| 3342 | /* |
| 3343 | * split the pointer block |
| 3344 | * allocate a new pointer block |
| 3345 | * move some of the pointer into the new block |
| 3346 | * prepare for updating the parent block |
| 3347 | */ |
| 3348 | for (;;) // do this twice when splitting block 1 |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3349 | { |
=?UTF-8?q?Dundar=20G=C3=B6c?= | f26c161 | 2022-04-07 13:26:34 +0100 | [diff] [blame] | 3350 | hp_new = ml_new_ptr(mfp); |
| 3351 | if (hp_new == NULL) // TODO: try to fix tree |
| 3352 | goto theend; |
| 3353 | pp_new = (PTR_BL *)(hp_new->bh_data); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3354 | |
=?UTF-8?q?Dundar=20G=C3=B6c?= | f26c161 | 2022-04-07 13:26:34 +0100 | [diff] [blame] | 3355 | if (hp->bh_bnum != 1) |
| 3356 | break; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3357 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3358 | /* |
=?UTF-8?q?Dundar=20G=C3=B6c?= | f26c161 | 2022-04-07 13:26:34 +0100 | [diff] [blame] | 3359 | * if block 1 becomes full the tree is given an extra level |
| 3360 | * The pointers from block 1 are moved into the new block. |
| 3361 | * block 1 is updated to point to the new block |
| 3362 | * then continue to split the new block |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3363 | */ |
=?UTF-8?q?Dundar=20G=C3=B6c?= | f26c161 | 2022-04-07 13:26:34 +0100 | [diff] [blame] | 3364 | mch_memmove(pp_new, pp, (size_t)page_size); |
| 3365 | pp->pb_count = 1; |
| 3366 | pp->pb_pointer[0].pe_bnum = hp_new->bh_bnum; |
| 3367 | pp->pb_pointer[0].pe_line_count = buf->b_ml.ml_line_count; |
| 3368 | pp->pb_pointer[0].pe_old_lnum = 1; |
| 3369 | pp->pb_pointer[0].pe_page_count = 1; |
| 3370 | mf_put(mfp, hp, TRUE, FALSE); // release block 1 |
| 3371 | hp = hp_new; // new block is to be split |
| 3372 | pp = pp_new; |
| 3373 | CHECK(stack_idx != 0, _("stack_idx should be 0")); |
| 3374 | ip->ip_index = 0; |
| 3375 | ++stack_idx; // do block 1 again later |
| 3376 | } |
| 3377 | /* |
| 3378 | * move the pointers after the current one to the new block |
| 3379 | * If there are none, the new entry will be in the new block. |
| 3380 | */ |
| 3381 | total_moved = pp->pb_count - pb_idx - 1; |
| 3382 | if (total_moved) |
| 3383 | { |
| 3384 | mch_memmove(&pp_new->pb_pointer[0], |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3385 | &pp->pb_pointer[pb_idx + 1], |
| 3386 | (size_t)(total_moved) * sizeof(PTR_EN)); |
=?UTF-8?q?Dundar=20G=C3=B6c?= | f26c161 | 2022-04-07 13:26:34 +0100 | [diff] [blame] | 3387 | pp_new->pb_count = total_moved; |
| 3388 | pp->pb_count -= total_moved - 1; |
| 3389 | pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right; |
| 3390 | pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right; |
| 3391 | pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right; |
| 3392 | if (lnum_right) |
| 3393 | pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3394 | } |
=?UTF-8?q?Dundar=20G=C3=B6c?= | f26c161 | 2022-04-07 13:26:34 +0100 | [diff] [blame] | 3395 | else |
| 3396 | { |
| 3397 | pp_new->pb_count = 1; |
| 3398 | pp_new->pb_pointer[0].pe_bnum = bnum_right; |
| 3399 | pp_new->pb_pointer[0].pe_line_count = line_count_right; |
| 3400 | pp_new->pb_pointer[0].pe_page_count = page_count_right; |
| 3401 | pp_new->pb_pointer[0].pe_old_lnum = lnum_right; |
| 3402 | } |
| 3403 | pp->pb_pointer[pb_idx].pe_bnum = bnum_left; |
| 3404 | pp->pb_pointer[pb_idx].pe_line_count = line_count_left; |
| 3405 | pp->pb_pointer[pb_idx].pe_page_count = page_count_left; |
| 3406 | if (lnum_left) |
| 3407 | pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left; |
| 3408 | lnum_left = 0; |
| 3409 | lnum_right = 0; |
| 3410 | |
| 3411 | /* |
| 3412 | * recompute line counts |
| 3413 | */ |
| 3414 | line_count_right = 0; |
| 3415 | for (i = 0; i < (int)pp_new->pb_count; ++i) |
| 3416 | line_count_right += pp_new->pb_pointer[i].pe_line_count; |
| 3417 | line_count_left = 0; |
| 3418 | for (i = 0; i < (int)pp->pb_count; ++i) |
| 3419 | line_count_left += pp->pb_pointer[i].pe_line_count; |
| 3420 | |
| 3421 | bnum_left = hp->bh_bnum; |
| 3422 | bnum_right = hp_new->bh_bnum; |
| 3423 | page_count_left = 1; |
| 3424 | page_count_right = 1; |
| 3425 | mf_put(mfp, hp, TRUE, FALSE); |
| 3426 | mf_put(mfp, hp_new, TRUE, FALSE); |
| 3427 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3428 | } |
| 3429 | |
| 3430 | /* |
| 3431 | * Safety check: fallen out of for loop? |
| 3432 | */ |
| 3433 | if (stack_idx < 0) |
| 3434 | { |
RestorerZ | 68ebcee | 2023-05-31 17:12:14 +0100 | [diff] [blame] | 3435 | iemsg(e_updated_too_many_blocks); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3436 | buf->b_ml.ml_stack_top = 0; // invalidate stack |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3437 | } |
| 3438 | } |
| 3439 | |
| 3440 | #ifdef FEAT_BYTEOFF |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3441 | // The line was inserted below 'lnum' |
Bram Moolenaar | 50652b0 | 2022-08-07 21:48:37 +0100 | [diff] [blame] | 3442 | ml_updatechunk(buf, lnum + 1, |
| 3443 | # ifdef FEAT_PROP_POPUP |
| 3444 | (long)text_len |
| 3445 | # else |
| 3446 | (long)len |
Bram Moolenaar | e5a0e8c | 2022-08-09 21:37:55 +0100 | [diff] [blame] | 3447 | # endif |
Bram Moolenaar | 50652b0 | 2022-08-07 21:48:37 +0100 | [diff] [blame] | 3448 | , ML_CHNK_ADDLINE); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3449 | #endif |
Bram Moolenaar | cdd8a5e | 2021-08-25 16:40:03 +0200 | [diff] [blame] | 3450 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3451 | #ifdef FEAT_NETBEANS_INTG |
Bram Moolenaar | b26e632 | 2010-05-22 21:34:09 +0200 | [diff] [blame] | 3452 | if (netbeans_active()) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3453 | { |
John Marriott | 02d7a6c | 2024-02-26 21:21:17 +0100 | [diff] [blame] | 3454 | int line_len = (int)STRLEN(line); |
| 3455 | if (line_len > 0) |
| 3456 | netbeans_inserted(buf, lnum+1, (colnr_T)0, line, line_len); |
| 3457 | netbeans_inserted(buf, lnum+1, (colnr_T)line_len, (char_u *)"\n", 1); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3458 | } |
| 3459 | #endif |
Bram Moolenaar | 509ce2a | 2016-03-11 22:52:15 +0100 | [diff] [blame] | 3460 | #ifdef FEAT_JOB_CHANNEL |
Bram Moolenaar | 99ef062 | 2016-03-06 20:22:25 +0100 | [diff] [blame] | 3461 | if (buf->b_write_to_channel) |
| 3462 | channel_write_new_lines(buf); |
| 3463 | #endif |
Bram Moolenaar | b56ac04 | 2018-12-28 23:22:40 +0100 | [diff] [blame] | 3464 | ret = OK; |
Bram Moolenaar | 99ef062 | 2016-03-06 20:22:25 +0100 | [diff] [blame] | 3465 | |
Bram Moolenaar | b56ac04 | 2018-12-28 23:22:40 +0100 | [diff] [blame] | 3466 | theend: |
Bram Moolenaar | 05ad5ff | 2019-11-30 22:48:27 +0100 | [diff] [blame] | 3467 | #ifdef FEAT_PROP_POPUP |
Bram Moolenaar | b56ac04 | 2018-12-28 23:22:40 +0100 | [diff] [blame] | 3468 | vim_free(tofree); |
| 3469 | #endif |
| 3470 | return ret; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3471 | } |
| 3472 | |
| 3473 | /* |
Bram Moolenaar | 250e311 | 2019-07-15 23:02:14 +0200 | [diff] [blame] | 3474 | * Flush any pending change and call ml_append_int() |
| 3475 | */ |
| 3476 | static int |
| 3477 | ml_append_flush( |
| 3478 | buf_T *buf, |
| 3479 | linenr_T lnum, // append after this line (can be 0) |
| 3480 | char_u *line, // text of the new line |
| 3481 | colnr_T len, // length of line, including NUL, or 0 |
Bram Moolenaar | a9d4b84 | 2020-05-30 14:46:52 +0200 | [diff] [blame] | 3482 | int flags) // ML_APPEND_ flags |
Bram Moolenaar | 250e311 | 2019-07-15 23:02:14 +0200 | [diff] [blame] | 3483 | { |
| 3484 | if (lnum > buf->b_ml.ml_line_count) |
| 3485 | return FAIL; // lnum out of range |
| 3486 | |
| 3487 | if (buf->b_ml.ml_line_lnum != 0) |
| 3488 | // This may also invoke ml_append_int(). |
| 3489 | ml_flush_line(buf); |
| 3490 | |
| 3491 | #ifdef FEAT_EVAL |
| 3492 | // When inserting above recorded changes: flush the changes before changing |
| 3493 | // the text. Then flush the cached line, it may become invalid. |
| 3494 | may_invoke_listeners(buf, lnum + 1, lnum + 1, 1); |
| 3495 | if (buf->b_ml.ml_line_lnum != 0) |
| 3496 | ml_flush_line(buf); |
| 3497 | #endif |
| 3498 | |
Bram Moolenaar | a9d4b84 | 2020-05-30 14:46:52 +0200 | [diff] [blame] | 3499 | return ml_append_int(buf, lnum, line, len, flags); |
Bram Moolenaar | 250e311 | 2019-07-15 23:02:14 +0200 | [diff] [blame] | 3500 | } |
| 3501 | |
| 3502 | /* |
| 3503 | * Append a line after lnum (may be 0 to insert a line in front of the file). |
| 3504 | * "line" does not need to be allocated, but can't be another line in a |
| 3505 | * buffer, unlocking may make it invalid. |
| 3506 | * |
Bram Moolenaar | a9d4b84 | 2020-05-30 14:46:52 +0200 | [diff] [blame] | 3507 | * "newfile": TRUE when starting to edit a new file, meaning that pe_old_lnum |
Bram Moolenaar | 250e311 | 2019-07-15 23:02:14 +0200 | [diff] [blame] | 3508 | * will be set for recovery |
| 3509 | * Check: The caller of this function should probably also call |
| 3510 | * appended_lines(). |
| 3511 | * |
| 3512 | * return FAIL for failure, OK otherwise |
| 3513 | */ |
| 3514 | int |
| 3515 | ml_append( |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3516 | linenr_T lnum, // append after this line (can be 0) |
| 3517 | char_u *line, // text of the new line |
| 3518 | colnr_T len, // length of new line, including NUL, or 0 |
| 3519 | int newfile) // flag, see above |
Bram Moolenaar | 250e311 | 2019-07-15 23:02:14 +0200 | [diff] [blame] | 3520 | { |
Bram Moolenaar | a9d4b84 | 2020-05-30 14:46:52 +0200 | [diff] [blame] | 3521 | return ml_append_flags(lnum, line, len, newfile ? ML_APPEND_NEW : 0); |
| 3522 | } |
| 3523 | |
| 3524 | int |
| 3525 | ml_append_flags( |
| 3526 | linenr_T lnum, // append after this line (can be 0) |
| 3527 | char_u *line, // text of the new line |
| 3528 | colnr_T len, // length of new line, including NUL, or 0 |
| 3529 | int flags) // ML_APPEND_ values |
| 3530 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3531 | // When starting up, we might still need to create the memfile |
Bram Moolenaar | 250e311 | 2019-07-15 23:02:14 +0200 | [diff] [blame] | 3532 | if (curbuf->b_ml.ml_mfp == NULL && open_buffer(FALSE, NULL, 0) == FAIL) |
| 3533 | return FAIL; |
Bram Moolenaar | a9d4b84 | 2020-05-30 14:46:52 +0200 | [diff] [blame] | 3534 | return ml_append_flush(curbuf, lnum, line, len, flags); |
Bram Moolenaar | 250e311 | 2019-07-15 23:02:14 +0200 | [diff] [blame] | 3535 | } |
| 3536 | |
Bram Moolenaar | a9d4b84 | 2020-05-30 14:46:52 +0200 | [diff] [blame] | 3537 | |
Bram Moolenaar | 9f28eeb | 2022-05-16 10:04:51 +0100 | [diff] [blame] | 3538 | #if defined(FEAT_SPELL) || defined(FEAT_QUICKFIX) || defined(FEAT_PROP_POPUP) \ |
| 3539 | || defined(PROTO) |
Bram Moolenaar | 250e311 | 2019-07-15 23:02:14 +0200 | [diff] [blame] | 3540 | /* |
| 3541 | * Like ml_append() but for an arbitrary buffer. The buffer must already have |
| 3542 | * a memline. |
| 3543 | */ |
| 3544 | int |
| 3545 | ml_append_buf( |
| 3546 | buf_T *buf, |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3547 | linenr_T lnum, // append after this line (can be 0) |
| 3548 | char_u *line, // text of the new line |
| 3549 | colnr_T len, // length of new line, including NUL, or 0 |
| 3550 | int newfile) // flag, see above |
Bram Moolenaar | 250e311 | 2019-07-15 23:02:14 +0200 | [diff] [blame] | 3551 | { |
| 3552 | if (buf->b_ml.ml_mfp == NULL) |
| 3553 | return FAIL; |
Bram Moolenaar | a9d4b84 | 2020-05-30 14:46:52 +0200 | [diff] [blame] | 3554 | return ml_append_flush(buf, lnum, line, len, newfile ? ML_APPEND_NEW : 0); |
Bram Moolenaar | 250e311 | 2019-07-15 23:02:14 +0200 | [diff] [blame] | 3555 | } |
| 3556 | #endif |
| 3557 | |
| 3558 | /* |
Bram Moolenaar | 5966ea1 | 2020-07-15 15:30:05 +0200 | [diff] [blame] | 3559 | * Replace line "lnum", with buffering, in current buffer. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3560 | * |
Bram Moolenaar | 1056d98 | 2006-03-09 22:37:52 +0000 | [diff] [blame] | 3561 | * If "copy" is TRUE, make a copy of the line, otherwise the line has been |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3562 | * copied to allocated memory already. |
Bram Moolenaar | 5966ea1 | 2020-07-15 15:30:05 +0200 | [diff] [blame] | 3563 | * If "copy" is FALSE the "line" may be freed to add text properties! |
| 3564 | * Do not use it after calling ml_replace(). |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3565 | * |
| 3566 | * Check: The caller of this function should probably also call |
Bram Moolenaar | a4d158b | 2022-08-14 14:17:45 +0100 | [diff] [blame] | 3567 | * changed_lines(), unless update_screen(UPD_NOT_VALID) is used. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3568 | * |
| 3569 | * return FAIL for failure, OK otherwise |
| 3570 | */ |
| 3571 | int |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 3572 | ml_replace(linenr_T lnum, char_u *line, int copy) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3573 | { |
Bram Moolenaar | 98aefe7 | 2018-12-13 22:20:09 +0100 | [diff] [blame] | 3574 | colnr_T len = -1; |
| 3575 | |
| 3576 | if (line != NULL) |
Bram Moolenaar | 4efe73b | 2018-12-16 14:37:39 +0100 | [diff] [blame] | 3577 | len = (colnr_T)STRLEN(line); |
Bram Moolenaar | ccae467 | 2019-01-04 15:09:57 +0100 | [diff] [blame] | 3578 | return ml_replace_len(lnum, line, len, FALSE, copy); |
Bram Moolenaar | 98aefe7 | 2018-12-13 22:20:09 +0100 | [diff] [blame] | 3579 | } |
| 3580 | |
Bram Moolenaar | ccae467 | 2019-01-04 15:09:57 +0100 | [diff] [blame] | 3581 | /* |
| 3582 | * Replace a line for the current buffer. Like ml_replace() with: |
| 3583 | * "len_arg" is the length of the text, excluding NUL. |
| 3584 | * If "has_props" is TRUE then "line_arg" includes the text properties and |
zeertzjq | 8c55d60 | 2024-03-13 20:42:26 +0100 | [diff] [blame] | 3585 | * "len_arg" includes the NUL of the text and text properties. |
Bram Moolenaar | fa4873c | 2022-06-30 22:13:59 +0100 | [diff] [blame] | 3586 | * When "copy" is TRUE copy the text into allocated memory, otherwise |
| 3587 | * "line_arg" must be allocated and will be consumed here. |
Bram Moolenaar | ccae467 | 2019-01-04 15:09:57 +0100 | [diff] [blame] | 3588 | */ |
Bram Moolenaar | 98aefe7 | 2018-12-13 22:20:09 +0100 | [diff] [blame] | 3589 | int |
Bram Moolenaar | ccae467 | 2019-01-04 15:09:57 +0100 | [diff] [blame] | 3590 | ml_replace_len( |
| 3591 | linenr_T lnum, |
| 3592 | char_u *line_arg, |
| 3593 | colnr_T len_arg, |
| 3594 | int has_props, |
| 3595 | int copy) |
Bram Moolenaar | 98aefe7 | 2018-12-13 22:20:09 +0100 | [diff] [blame] | 3596 | { |
| 3597 | char_u *line = line_arg; |
| 3598 | colnr_T len = len_arg; |
| 3599 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3600 | if (line == NULL) // just checking... |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3601 | return FAIL; |
| 3602 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3603 | // When starting up, we might still need to create the memfile |
Bram Moolenaar | 59f931e | 2010-07-24 20:27:03 +0200 | [diff] [blame] | 3604 | if (curbuf->b_ml.ml_mfp == NULL && open_buffer(FALSE, NULL, 0) == FAIL) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3605 | return FAIL; |
| 3606 | |
Bram Moolenaar | ccae467 | 2019-01-04 15:09:57 +0100 | [diff] [blame] | 3607 | if (!has_props) |
| 3608 | ++len; // include the NUL after the text |
| 3609 | if (copy) |
| 3610 | { |
| 3611 | // copy the line to allocated memory |
Bram Moolenaar | 05ad5ff | 2019-11-30 22:48:27 +0100 | [diff] [blame] | 3612 | #ifdef FEAT_PROP_POPUP |
Bram Moolenaar | ccae467 | 2019-01-04 15:09:57 +0100 | [diff] [blame] | 3613 | if (has_props) |
| 3614 | line = vim_memsave(line, len); |
| 3615 | else |
| 3616 | #endif |
| 3617 | line = vim_strnsave(line, len - 1); |
| 3618 | if (line == NULL) |
| 3619 | return FAIL; |
| 3620 | } |
| 3621 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3622 | #ifdef FEAT_NETBEANS_INTG |
Bram Moolenaar | b26e632 | 2010-05-22 21:34:09 +0200 | [diff] [blame] | 3623 | if (netbeans_active()) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3624 | { |
John Marriott | 02d7a6c | 2024-02-26 21:21:17 +0100 | [diff] [blame] | 3625 | netbeans_removed(curbuf, lnum, 0, (long)ml_get_len(lnum)); |
Bram Moolenaar | a93fa7e | 2006-04-17 22:14:47 +0000 | [diff] [blame] | 3626 | netbeans_inserted(curbuf, lnum, 0, line, (int)STRLEN(line)); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3627 | } |
| 3628 | #endif |
Bram Moolenaar | 98aefe7 | 2018-12-13 22:20:09 +0100 | [diff] [blame] | 3629 | if (curbuf->b_ml.ml_line_lnum != lnum) |
| 3630 | { |
| 3631 | // another line is buffered, flush it |
| 3632 | ml_flush_line(curbuf); |
| 3633 | |
Bram Moolenaar | 05ad5ff | 2019-11-30 22:48:27 +0100 | [diff] [blame] | 3634 | #ifdef FEAT_PROP_POPUP |
Bram Moolenaar | ccae467 | 2019-01-04 15:09:57 +0100 | [diff] [blame] | 3635 | if (curbuf->b_has_textprop && !has_props) |
Bram Moolenaar | 98aefe7 | 2018-12-13 22:20:09 +0100 | [diff] [blame] | 3636 | // Need to fetch the old line to copy over any text properties. |
| 3637 | ml_get_buf(curbuf, lnum, TRUE); |
| 3638 | #endif |
| 3639 | } |
| 3640 | |
Bram Moolenaar | 05ad5ff | 2019-11-30 22:48:27 +0100 | [diff] [blame] | 3641 | #ifdef FEAT_PROP_POPUP |
Bram Moolenaar | ccae467 | 2019-01-04 15:09:57 +0100 | [diff] [blame] | 3642 | if (curbuf->b_has_textprop && !has_props) |
Bram Moolenaar | 98aefe7 | 2018-12-13 22:20:09 +0100 | [diff] [blame] | 3643 | { |
| 3644 | size_t oldtextlen = STRLEN(curbuf->b_ml.ml_line_ptr) + 1; |
| 3645 | |
| 3646 | if (oldtextlen < (size_t)curbuf->b_ml.ml_line_len) |
| 3647 | { |
| 3648 | char_u *newline; |
| 3649 | size_t textproplen = curbuf->b_ml.ml_line_len - oldtextlen; |
| 3650 | |
| 3651 | // Need to copy over text properties, stored after the text. |
Bram Moolenaar | ccae467 | 2019-01-04 15:09:57 +0100 | [diff] [blame] | 3652 | newline = alloc(len + (int)textproplen); |
Bram Moolenaar | 98aefe7 | 2018-12-13 22:20:09 +0100 | [diff] [blame] | 3653 | if (newline != NULL) |
| 3654 | { |
Bram Moolenaar | ccae467 | 2019-01-04 15:09:57 +0100 | [diff] [blame] | 3655 | mch_memmove(newline, line, len); |
Bram Moolenaar | 0d3cb73 | 2019-05-18 13:05:18 +0200 | [diff] [blame] | 3656 | mch_memmove(newline + len, curbuf->b_ml.ml_line_ptr |
| 3657 | + oldtextlen, textproplen); |
Bram Moolenaar | 98aefe7 | 2018-12-13 22:20:09 +0100 | [diff] [blame] | 3658 | vim_free(line); |
| 3659 | line = newline; |
Bram Moolenaar | 4efe73b | 2018-12-16 14:37:39 +0100 | [diff] [blame] | 3660 | len += (colnr_T)textproplen; |
Bram Moolenaar | 98aefe7 | 2018-12-13 22:20:09 +0100 | [diff] [blame] | 3661 | } |
| 3662 | } |
| 3663 | } |
| 3664 | #endif |
| 3665 | |
Bram Moolenaar | fa4873c | 2022-06-30 22:13:59 +0100 | [diff] [blame] | 3666 | if (curbuf->b_ml.ml_flags & (ML_LINE_DIRTY | ML_ALLOCATED)) |
| 3667 | vim_free(curbuf->b_ml.ml_line_ptr); // free allocated line |
Bram Moolenaar | 98aefe7 | 2018-12-13 22:20:09 +0100 | [diff] [blame] | 3668 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3669 | curbuf->b_ml.ml_line_ptr = line; |
Bram Moolenaar | ccae467 | 2019-01-04 15:09:57 +0100 | [diff] [blame] | 3670 | curbuf->b_ml.ml_line_len = len; |
zeertzjq | 94b7c32 | 2024-03-12 21:50:32 +0100 | [diff] [blame] | 3671 | curbuf->b_ml.ml_line_textlen = !has_props ? len_arg + 1 : 0; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3672 | curbuf->b_ml.ml_line_lnum = lnum; |
| 3673 | curbuf->b_ml.ml_flags = (curbuf->b_ml.ml_flags | ML_LINE_DIRTY) & ~ML_EMPTY; |
| 3674 | |
| 3675 | return OK; |
| 3676 | } |
| 3677 | |
Bram Moolenaar | 05ad5ff | 2019-11-30 22:48:27 +0100 | [diff] [blame] | 3678 | #ifdef FEAT_PROP_POPUP |
Bram Moolenaar | c1a9bc1 | 2018-12-28 21:59:29 +0100 | [diff] [blame] | 3679 | /* |
| 3680 | * Adjust text properties in line "lnum" for a deleted line. |
Paul Ollis | 1bdc60e | 2022-05-15 22:24:55 +0100 | [diff] [blame] | 3681 | * When "above" is true this is the line above the deleted line, otherwise this |
| 3682 | * is the line below the deleted line. |
| 3683 | * "del_props[del_props_len]" are the properties of the deleted line. |
Bram Moolenaar | c1a9bc1 | 2018-12-28 21:59:29 +0100 | [diff] [blame] | 3684 | */ |
| 3685 | static void |
| 3686 | adjust_text_props_for_delete( |
| 3687 | buf_T *buf, |
| 3688 | linenr_T lnum, |
| 3689 | char_u *del_props, |
| 3690 | int del_props_len, |
| 3691 | int above) |
| 3692 | { |
| 3693 | int did_get_line = FALSE; |
| 3694 | int done_del; |
| 3695 | int done_this; |
| 3696 | textprop_T prop_del; |
Bram Moolenaar | c1a9bc1 | 2018-12-28 21:59:29 +0100 | [diff] [blame] | 3697 | bhdr_T *hp; |
| 3698 | DATA_BL *dp; |
| 3699 | int idx; |
| 3700 | int line_start; |
| 3701 | long line_size; |
Christian Brabandt | a2a90d5 | 2023-06-26 18:48:09 +0100 | [diff] [blame] | 3702 | int this_props_len = 0; |
Bram Moolenaar | c1a9bc1 | 2018-12-28 21:59:29 +0100 | [diff] [blame] | 3703 | char_u *text; |
| 3704 | size_t textlen; |
| 3705 | int found; |
| 3706 | |
| 3707 | for (done_del = 0; done_del < del_props_len; done_del += sizeof(textprop_T)) |
| 3708 | { |
| 3709 | mch_memmove(&prop_del, del_props + done_del, sizeof(textprop_T)); |
| 3710 | if ((above && (prop_del.tp_flags & TP_FLAG_CONT_PREV) |
| 3711 | && !(prop_del.tp_flags & TP_FLAG_CONT_NEXT)) |
| 3712 | || (!above && (prop_del.tp_flags & TP_FLAG_CONT_NEXT) |
| 3713 | && !(prop_del.tp_flags & TP_FLAG_CONT_PREV))) |
| 3714 | { |
| 3715 | if (!did_get_line) |
| 3716 | { |
| 3717 | did_get_line = TRUE; |
| 3718 | if ((hp = ml_find_line(buf, lnum, ML_FIND)) == NULL) |
| 3719 | return; |
| 3720 | |
| 3721 | dp = (DATA_BL *)(hp->bh_data); |
| 3722 | idx = lnum - buf->b_ml.ml_locked_low; |
| 3723 | line_start = ((dp->db_index[idx]) & DB_INDEX_MASK); |
| 3724 | if (idx == 0) // first line in block, text at the end |
| 3725 | line_size = dp->db_txt_end - line_start; |
| 3726 | else |
Bram Moolenaar | 87be9be | 2020-05-30 15:32:02 +0200 | [diff] [blame] | 3727 | line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK) |
| 3728 | - line_start; |
Bram Moolenaar | c1a9bc1 | 2018-12-28 21:59:29 +0100 | [diff] [blame] | 3729 | text = (char_u *)dp + line_start; |
| 3730 | textlen = STRLEN(text) + 1; |
| 3731 | if ((long)textlen >= line_size) |
| 3732 | { |
| 3733 | if (above) |
| 3734 | internal_error("no text property above deleted line"); |
| 3735 | else |
| 3736 | internal_error("no text property below deleted line"); |
| 3737 | return; |
| 3738 | } |
Bram Moolenaar | 4b7214e | 2019-01-03 21:55:32 +0100 | [diff] [blame] | 3739 | this_props_len = line_size - (int)textlen; |
Bram Moolenaar | c1a9bc1 | 2018-12-28 21:59:29 +0100 | [diff] [blame] | 3740 | } |
| 3741 | |
| 3742 | found = FALSE; |
Bram Moolenaar | 87be9be | 2020-05-30 15:32:02 +0200 | [diff] [blame] | 3743 | for (done_this = 0; done_this < this_props_len; |
| 3744 | done_this += sizeof(textprop_T)) |
Bram Moolenaar | c1a9bc1 | 2018-12-28 21:59:29 +0100 | [diff] [blame] | 3745 | { |
Bram Moolenaar | 87be9be | 2020-05-30 15:32:02 +0200 | [diff] [blame] | 3746 | int flag = above ? TP_FLAG_CONT_NEXT |
| 3747 | : TP_FLAG_CONT_PREV; |
| 3748 | textprop_T prop_this; |
| 3749 | |
Paul Ollis | 1bdc60e | 2022-05-15 22:24:55 +0100 | [diff] [blame] | 3750 | mch_memmove(&prop_this, text + textlen + done_this, |
Bram Moolenaar | 87be9be | 2020-05-30 15:32:02 +0200 | [diff] [blame] | 3751 | sizeof(textprop_T)); |
| 3752 | if ((prop_this.tp_flags & flag) |
| 3753 | && prop_del.tp_id == prop_this.tp_id |
Bram Moolenaar | c1a9bc1 | 2018-12-28 21:59:29 +0100 | [diff] [blame] | 3754 | && prop_del.tp_type == prop_this.tp_type) |
| 3755 | { |
Bram Moolenaar | c1a9bc1 | 2018-12-28 21:59:29 +0100 | [diff] [blame] | 3756 | found = TRUE; |
Bram Moolenaar | 87be9be | 2020-05-30 15:32:02 +0200 | [diff] [blame] | 3757 | prop_this.tp_flags &= ~flag; |
Paul Ollis | 1bdc60e | 2022-05-15 22:24:55 +0100 | [diff] [blame] | 3758 | mch_memmove(text + textlen + done_this, &prop_this, |
Bram Moolenaar | 87be9be | 2020-05-30 15:32:02 +0200 | [diff] [blame] | 3759 | sizeof(textprop_T)); |
| 3760 | break; |
Bram Moolenaar | c1a9bc1 | 2018-12-28 21:59:29 +0100 | [diff] [blame] | 3761 | } |
| 3762 | } |
| 3763 | if (!found) |
| 3764 | { |
| 3765 | if (above) |
| 3766 | internal_error("text property above deleted line not found"); |
| 3767 | else |
| 3768 | internal_error("text property below deleted line not found"); |
| 3769 | } |
| 3770 | |
| 3771 | buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS); |
| 3772 | } |
| 3773 | } |
| 3774 | } |
| 3775 | #endif |
| 3776 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3777 | /* |
Bram Moolenaar | 4033c55 | 2017-09-16 20:54:51 +0200 | [diff] [blame] | 3778 | * Delete line "lnum" in the current buffer. |
Bram Moolenaar | a9d4b84 | 2020-05-30 14:46:52 +0200 | [diff] [blame] | 3779 | * When "flags" has ML_DEL_MESSAGE may give a "No lines in buffer" message. |
| 3780 | * When "flags" has ML_DEL_UNDO this is called from undo. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3781 | * |
| 3782 | * return FAIL for failure, OK otherwise |
| 3783 | */ |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3784 | static int |
Bram Moolenaar | a9d4b84 | 2020-05-30 14:46:52 +0200 | [diff] [blame] | 3785 | ml_delete_int(buf_T *buf, linenr_T lnum, int flags) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3786 | { |
| 3787 | bhdr_T *hp; |
| 3788 | memfile_T *mfp; |
| 3789 | DATA_BL *dp; |
| 3790 | PTR_BL *pp; |
| 3791 | infoptr_T *ip; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3792 | int count; // number of entries in block |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3793 | int idx; |
| 3794 | int stack_idx; |
| 3795 | int text_start; |
| 3796 | int line_start; |
| 3797 | long line_size; |
| 3798 | int i; |
Bram Moolenaar | c1a9bc1 | 2018-12-28 21:59:29 +0100 | [diff] [blame] | 3799 | int ret = FAIL; |
Bram Moolenaar | 05ad5ff | 2019-11-30 22:48:27 +0100 | [diff] [blame] | 3800 | #ifdef FEAT_PROP_POPUP |
Bram Moolenaar | c1a9bc1 | 2018-12-28 21:59:29 +0100 | [diff] [blame] | 3801 | char_u *textprop_save = NULL; |
Bram Moolenaar | e5a0e8c | 2022-08-09 21:37:55 +0100 | [diff] [blame] | 3802 | long textprop_len = 0; |
Bram Moolenaar | c1a9bc1 | 2018-12-28 21:59:29 +0100 | [diff] [blame] | 3803 | #endif |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3804 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3805 | if (lowest_marked && lowest_marked > lnum) |
| 3806 | lowest_marked--; |
| 3807 | |
| 3808 | /* |
| 3809 | * If the file becomes empty the last line is replaced by an empty line. |
| 3810 | */ |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3811 | if (buf->b_ml.ml_line_count == 1) // file becomes empty |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3812 | { |
Bram Moolenaar | a9d4b84 | 2020-05-30 14:46:52 +0200 | [diff] [blame] | 3813 | if ((flags & ML_DEL_MESSAGE) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3814 | #ifdef FEAT_NETBEANS_INTG |
| 3815 | && !netbeansSuppressNoLines |
| 3816 | #endif |
| 3817 | ) |
Bram Moolenaar | 238a564 | 2006-02-21 22:12:05 +0000 | [diff] [blame] | 3818 | set_keep_msg((char_u *)_(no_lines_msg), 0); |
| 3819 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3820 | // FEAT_BYTEOFF already handled in there, don't worry 'bout it below |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3821 | i = ml_replace((linenr_T)1, (char_u *)"", TRUE); |
| 3822 | buf->b_ml.ml_flags |= ML_EMPTY; |
| 3823 | |
| 3824 | return i; |
| 3825 | } |
| 3826 | |
| 3827 | /* |
Bram Moolenaar | c1a9bc1 | 2018-12-28 21:59:29 +0100 | [diff] [blame] | 3828 | * Find the data block containing the line. |
| 3829 | * This also fills the stack with the blocks from the root to the data block. |
| 3830 | * This also releases any locked block.. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3831 | */ |
| 3832 | mfp = buf->b_ml.ml_mfp; |
| 3833 | if (mfp == NULL) |
| 3834 | return FAIL; |
| 3835 | |
| 3836 | if ((hp = ml_find_line(buf, lnum, ML_DELETE)) == NULL) |
| 3837 | return FAIL; |
| 3838 | |
| 3839 | dp = (DATA_BL *)(hp->bh_data); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3840 | // compute line count before the delete |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3841 | count = (long)(buf->b_ml.ml_locked_high) |
| 3842 | - (long)(buf->b_ml.ml_locked_low) + 2; |
| 3843 | idx = lnum - buf->b_ml.ml_locked_low; |
| 3844 | |
| 3845 | --buf->b_ml.ml_line_count; |
| 3846 | |
| 3847 | line_start = ((dp->db_index[idx]) & DB_INDEX_MASK); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3848 | if (idx == 0) // first line in block, text at the end |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3849 | line_size = dp->db_txt_end - line_start; |
| 3850 | else |
| 3851 | line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK) - line_start; |
| 3852 | |
| 3853 | #ifdef FEAT_NETBEANS_INTG |
Bram Moolenaar | b26e632 | 2010-05-22 21:34:09 +0200 | [diff] [blame] | 3854 | if (netbeans_active()) |
=?UTF-8?q?Dundar=20G=C3=B6c?= | 420fabc | 2022-01-28 15:28:04 +0000 | [diff] [blame] | 3855 | netbeans_removed(buf, lnum, 0, line_size); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3856 | #endif |
Bram Moolenaar | 05ad5ff | 2019-11-30 22:48:27 +0100 | [diff] [blame] | 3857 | #ifdef FEAT_PROP_POPUP |
Bram Moolenaar | e5a0e8c | 2022-08-09 21:37:55 +0100 | [diff] [blame] | 3858 | // If there are text properties compute their byte length. |
| 3859 | // if needed make a copy, so that we can update properties in preceding and |
| 3860 | // following lines. |
| 3861 | if (buf->b_has_textprop) |
Bram Moolenaar | c1a9bc1 | 2018-12-28 21:59:29 +0100 | [diff] [blame] | 3862 | { |
| 3863 | size_t textlen = STRLEN((char_u *)dp + line_start) + 1; |
| 3864 | |
Bram Moolenaar | e5a0e8c | 2022-08-09 21:37:55 +0100 | [diff] [blame] | 3865 | textprop_len = line_size - (long)textlen; |
| 3866 | if (!(flags & (ML_DEL_UNDO | ML_DEL_NOPROP)) && textprop_len > 0) |
Bram Moolenaar | c1a9bc1 | 2018-12-28 21:59:29 +0100 | [diff] [blame] | 3867 | textprop_save = vim_memsave((char_u *)dp + line_start + textlen, |
Bram Moolenaar | e5a0e8c | 2022-08-09 21:37:55 +0100 | [diff] [blame] | 3868 | textprop_len); |
Bram Moolenaar | c1a9bc1 | 2018-12-28 21:59:29 +0100 | [diff] [blame] | 3869 | } |
| 3870 | #endif |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3871 | |
| 3872 | /* |
| 3873 | * special case: If there is only one line in the data block it becomes empty. |
| 3874 | * Then we have to remove the entry, pointing to this data block, from the |
| 3875 | * pointer block. If this pointer block also becomes empty, we go up another |
| 3876 | * block, and so on, up to the root if necessary. |
| 3877 | * The line counts in the pointer blocks have already been adjusted by |
| 3878 | * ml_find_line(). |
| 3879 | */ |
| 3880 | if (count == 1) |
| 3881 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3882 | mf_free(mfp, hp); // free the data block |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3883 | buf->b_ml.ml_locked = NULL; |
| 3884 | |
Bram Moolenaar | e60acc1 | 2011-05-10 16:41:25 +0200 | [diff] [blame] | 3885 | for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0; |
| 3886 | --stack_idx) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3887 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3888 | buf->b_ml.ml_stack_top = 0; // stack is invalid when failing |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3889 | ip = &(buf->b_ml.ml_stack[stack_idx]); |
| 3890 | idx = ip->ip_index; |
| 3891 | if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL) |
Bram Moolenaar | c1a9bc1 | 2018-12-28 21:59:29 +0100 | [diff] [blame] | 3892 | goto theend; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3893 | pp = (PTR_BL *)(hp->bh_data); // must be pointer block |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3894 | if (pp->pb_id != PTR_ID) |
| 3895 | { |
RestorerZ | 68ebcee | 2023-05-31 17:12:14 +0100 | [diff] [blame] | 3896 | iemsg(e_pointer_block_id_wrong_four); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3897 | mf_put(mfp, hp, FALSE, FALSE); |
Bram Moolenaar | c1a9bc1 | 2018-12-28 21:59:29 +0100 | [diff] [blame] | 3898 | goto theend; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3899 | } |
| 3900 | count = --(pp->pb_count); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3901 | if (count == 0) // the pointer block becomes empty! |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3902 | mf_free(mfp, hp); |
| 3903 | else |
| 3904 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3905 | if (count != idx) // move entries after the deleted one |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3906 | mch_memmove(&pp->pb_pointer[idx], &pp->pb_pointer[idx + 1], |
| 3907 | (size_t)(count - idx) * sizeof(PTR_EN)); |
| 3908 | mf_put(mfp, hp, TRUE, FALSE); |
| 3909 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 3910 | buf->b_ml.ml_stack_top = stack_idx; // truncate stack |
| 3911 | // fix line count for rest of blocks in the stack |
Bram Moolenaar | 6b803a7 | 2007-05-06 14:25:46 +0000 | [diff] [blame] | 3912 | if (buf->b_ml.ml_locked_lineadd != 0) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3913 | { |
| 3914 | ml_lineadd(buf, buf->b_ml.ml_locked_lineadd); |
| 3915 | buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high += |
Bram Moolenaar | 6b803a7 | 2007-05-06 14:25:46 +0000 | [diff] [blame] | 3916 | buf->b_ml.ml_locked_lineadd; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3917 | } |
| 3918 | ++(buf->b_ml.ml_stack_top); |
| 3919 | |
| 3920 | break; |
| 3921 | } |
| 3922 | } |
| 3923 | CHECK(stack_idx < 0, _("deleted block 1?")); |
| 3924 | } |
| 3925 | else |
| 3926 | { |
| 3927 | /* |
| 3928 | * delete the text by moving the next lines forwards |
| 3929 | */ |
| 3930 | text_start = dp->db_txt_start; |
| 3931 | mch_memmove((char *)dp + text_start + line_size, |
| 3932 | (char *)dp + text_start, (size_t)(line_start - text_start)); |
| 3933 | |
| 3934 | /* |
| 3935 | * delete the index by moving the next indexes backwards |
| 3936 | * Adjust the indexes for the text movement. |
| 3937 | */ |
| 3938 | for (i = idx; i < count - 1; ++i) |
| 3939 | dp->db_index[i] = dp->db_index[i + 1] + line_size; |
| 3940 | |
| 3941 | dp->db_free += line_size + INDEX_SIZE; |
| 3942 | dp->db_txt_start += line_size; |
| 3943 | --(dp->db_line_count); |
| 3944 | |
| 3945 | /* |
| 3946 | * mark the block dirty and make sure it is in the file (for recovery) |
| 3947 | */ |
| 3948 | buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS); |
| 3949 | } |
| 3950 | |
| 3951 | #ifdef FEAT_BYTEOFF |
Bram Moolenaar | cdd8a5e | 2021-08-25 16:40:03 +0200 | [diff] [blame] | 3952 | ml_updatechunk(buf, lnum, line_size |
| 3953 | # ifdef FEAT_PROP_POPUP |
Bram Moolenaar | e5a0e8c | 2022-08-09 21:37:55 +0100 | [diff] [blame] | 3954 | - textprop_len |
Bram Moolenaar | cdd8a5e | 2021-08-25 16:40:03 +0200 | [diff] [blame] | 3955 | # endif |
Bram Moolenaar | e5a0e8c | 2022-08-09 21:37:55 +0100 | [diff] [blame] | 3956 | , ML_CHNK_DELLINE); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3957 | #endif |
Bram Moolenaar | c1a9bc1 | 2018-12-28 21:59:29 +0100 | [diff] [blame] | 3958 | ret = OK; |
| 3959 | |
| 3960 | theend: |
Bram Moolenaar | 05ad5ff | 2019-11-30 22:48:27 +0100 | [diff] [blame] | 3961 | #ifdef FEAT_PROP_POPUP |
Bram Moolenaar | c1a9bc1 | 2018-12-28 21:59:29 +0100 | [diff] [blame] | 3962 | if (textprop_save != NULL) |
| 3963 | { |
| 3964 | // Adjust text properties in the line above and below. |
| 3965 | if (lnum > 1) |
Bram Moolenaar | 4cd5c52 | 2021-06-27 13:04:00 +0200 | [diff] [blame] | 3966 | adjust_text_props_for_delete(buf, lnum - 1, textprop_save, |
Bram Moolenaar | e5a0e8c | 2022-08-09 21:37:55 +0100 | [diff] [blame] | 3967 | (int)textprop_len, TRUE); |
Bram Moolenaar | c1a9bc1 | 2018-12-28 21:59:29 +0100 | [diff] [blame] | 3968 | if (lnum <= buf->b_ml.ml_line_count) |
Bram Moolenaar | 4cd5c52 | 2021-06-27 13:04:00 +0200 | [diff] [blame] | 3969 | adjust_text_props_for_delete(buf, lnum, textprop_save, |
Bram Moolenaar | e5a0e8c | 2022-08-09 21:37:55 +0100 | [diff] [blame] | 3970 | (int)textprop_len, FALSE); |
Bram Moolenaar | c1a9bc1 | 2018-12-28 21:59:29 +0100 | [diff] [blame] | 3971 | } |
| 3972 | vim_free(textprop_save); |
| 3973 | #endif |
| 3974 | return ret; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 3975 | } |
| 3976 | |
| 3977 | /* |
Bram Moolenaar | a9d4b84 | 2020-05-30 14:46:52 +0200 | [diff] [blame] | 3978 | * Delete line "lnum" in the current buffer. |
| 3979 | * When "message" is TRUE may give a "No lines in buffer" message. |
| 3980 | * |
| 3981 | * Check: The caller of this function should probably also call |
| 3982 | * deleted_lines() after this. |
| 3983 | * |
| 3984 | * return FAIL for failure, OK otherwise |
| 3985 | */ |
| 3986 | int |
Bram Moolenaar | ca70c07 | 2020-05-30 20:30:46 +0200 | [diff] [blame] | 3987 | ml_delete(linenr_T lnum) |
Bram Moolenaar | a9d4b84 | 2020-05-30 14:46:52 +0200 | [diff] [blame] | 3988 | { |
Bram Moolenaar | ca70c07 | 2020-05-30 20:30:46 +0200 | [diff] [blame] | 3989 | return ml_delete_flags(lnum, 0); |
Bram Moolenaar | a9d4b84 | 2020-05-30 14:46:52 +0200 | [diff] [blame] | 3990 | } |
| 3991 | |
| 3992 | /* |
| 3993 | * Like ml_delete() but using flags (see ml_delete_int()). |
| 3994 | */ |
| 3995 | int |
| 3996 | ml_delete_flags(linenr_T lnum, int flags) |
| 3997 | { |
| 3998 | ml_flush_line(curbuf); |
| 3999 | if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) |
| 4000 | return FAIL; |
| 4001 | |
| 4002 | #ifdef FEAT_EVAL |
| 4003 | // When inserting above recorded changes: flush the changes before changing |
| 4004 | // the text. |
| 4005 | may_invoke_listeners(curbuf, lnum, lnum + 1, -1); |
| 4006 | #endif |
| 4007 | |
| 4008 | return ml_delete_int(curbuf, lnum, flags); |
| 4009 | } |
| 4010 | |
| 4011 | /* |
Bram Moolenaar | c1a9bc1 | 2018-12-28 21:59:29 +0100 | [diff] [blame] | 4012 | * set the DB_MARKED flag for line 'lnum' |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4013 | */ |
| 4014 | void |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 4015 | ml_setmarked(linenr_T lnum) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4016 | { |
| 4017 | bhdr_T *hp; |
| 4018 | DATA_BL *dp; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4019 | // invalid line number |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4020 | if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count |
| 4021 | || curbuf->b_ml.ml_mfp == NULL) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4022 | return; // give error message? |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4023 | |
| 4024 | if (lowest_marked == 0 || lowest_marked > lnum) |
| 4025 | lowest_marked = lnum; |
| 4026 | |
| 4027 | /* |
| 4028 | * find the data block containing the line |
| 4029 | * This also fills the stack with the blocks from the root to the data block |
| 4030 | * This also releases any locked block. |
| 4031 | */ |
| 4032 | if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4033 | return; // give error message? |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4034 | |
| 4035 | dp = (DATA_BL *)(hp->bh_data); |
| 4036 | dp->db_index[lnum - curbuf->b_ml.ml_locked_low] |= DB_MARKED; |
| 4037 | curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY; |
| 4038 | } |
| 4039 | |
| 4040 | /* |
Bram Moolenaar | c1a9bc1 | 2018-12-28 21:59:29 +0100 | [diff] [blame] | 4041 | * find the first line with its DB_MARKED flag set |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4042 | */ |
| 4043 | linenr_T |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 4044 | ml_firstmarked(void) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4045 | { |
| 4046 | bhdr_T *hp; |
| 4047 | DATA_BL *dp; |
| 4048 | linenr_T lnum; |
| 4049 | int i; |
| 4050 | |
| 4051 | if (curbuf->b_ml.ml_mfp == NULL) |
| 4052 | return (linenr_T) 0; |
| 4053 | |
| 4054 | /* |
| 4055 | * The search starts with lowest_marked line. This is the last line where |
| 4056 | * a mark was found, adjusted by inserting/deleting lines. |
| 4057 | */ |
| 4058 | for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count; ) |
| 4059 | { |
| 4060 | /* |
| 4061 | * Find the data block containing the line. |
| 4062 | * This also fills the stack with the blocks from the root to the data |
| 4063 | * block This also releases any locked block. |
| 4064 | */ |
| 4065 | if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4066 | return (linenr_T)0; // give error message? |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4067 | |
| 4068 | dp = (DATA_BL *)(hp->bh_data); |
| 4069 | |
| 4070 | for (i = lnum - curbuf->b_ml.ml_locked_low; |
| 4071 | lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum) |
| 4072 | if ((dp->db_index[i]) & DB_MARKED) |
| 4073 | { |
| 4074 | (dp->db_index[i]) &= DB_INDEX_MASK; |
| 4075 | curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY; |
| 4076 | lowest_marked = lnum + 1; |
| 4077 | return lnum; |
| 4078 | } |
| 4079 | } |
| 4080 | |
| 4081 | return (linenr_T) 0; |
| 4082 | } |
| 4083 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4084 | /* |
| 4085 | * clear all DB_MARKED flags |
| 4086 | */ |
| 4087 | void |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 4088 | ml_clearmarked(void) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4089 | { |
| 4090 | bhdr_T *hp; |
| 4091 | DATA_BL *dp; |
| 4092 | linenr_T lnum; |
| 4093 | int i; |
| 4094 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4095 | if (curbuf->b_ml.ml_mfp == NULL) // nothing to do |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4096 | return; |
| 4097 | |
| 4098 | /* |
| 4099 | * The search starts with line lowest_marked. |
| 4100 | */ |
| 4101 | for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count; ) |
| 4102 | { |
| 4103 | /* |
| 4104 | * Find the data block containing the line. |
| 4105 | * This also fills the stack with the blocks from the root to the data |
| 4106 | * block and releases any locked block. |
| 4107 | */ |
| 4108 | if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4109 | return; // give error message? |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4110 | |
| 4111 | dp = (DATA_BL *)(hp->bh_data); |
| 4112 | |
| 4113 | for (i = lnum - curbuf->b_ml.ml_locked_low; |
| 4114 | lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum) |
| 4115 | if ((dp->db_index[i]) & DB_MARKED) |
| 4116 | { |
| 4117 | (dp->db_index[i]) &= DB_INDEX_MASK; |
| 4118 | curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY; |
| 4119 | } |
| 4120 | } |
| 4121 | |
| 4122 | lowest_marked = 0; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4123 | } |
| 4124 | |
| 4125 | /* |
| 4126 | * flush ml_line if necessary |
| 4127 | */ |
| 4128 | static void |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 4129 | ml_flush_line(buf_T *buf) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4130 | { |
| 4131 | bhdr_T *hp; |
| 4132 | DATA_BL *dp; |
| 4133 | linenr_T lnum; |
| 4134 | char_u *new_line; |
| 4135 | char_u *old_line; |
| 4136 | colnr_T new_len; |
| 4137 | int old_len; |
| 4138 | int extra; |
| 4139 | int idx; |
| 4140 | int start; |
| 4141 | int count; |
| 4142 | int i; |
Bram Moolenaar | 0ca4b35 | 2010-02-11 18:54:43 +0100 | [diff] [blame] | 4143 | static int entered = FALSE; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4144 | |
| 4145 | if (buf->b_ml.ml_line_lnum == 0 || buf->b_ml.ml_mfp == NULL) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4146 | return; // nothing to do |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4147 | |
| 4148 | if (buf->b_ml.ml_flags & ML_LINE_DIRTY) |
| 4149 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4150 | // This code doesn't work recursively, but Netbeans may call back here |
| 4151 | // when obtaining the cursor position. |
Bram Moolenaar | 0ca4b35 | 2010-02-11 18:54:43 +0100 | [diff] [blame] | 4152 | if (entered) |
| 4153 | return; |
| 4154 | entered = TRUE; |
| 4155 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4156 | lnum = buf->b_ml.ml_line_lnum; |
| 4157 | new_line = buf->b_ml.ml_line_ptr; |
| 4158 | |
| 4159 | hp = ml_find_line(buf, lnum, ML_FIND); |
| 4160 | if (hp == NULL) |
RestorerZ | 68ebcee | 2023-05-31 17:12:14 +0100 | [diff] [blame] | 4161 | siemsg(e_cannot_find_line_nr, lnum); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4162 | else |
| 4163 | { |
| 4164 | dp = (DATA_BL *)(hp->bh_data); |
| 4165 | idx = lnum - buf->b_ml.ml_locked_low; |
| 4166 | start = ((dp->db_index[idx]) & DB_INDEX_MASK); |
| 4167 | old_line = (char_u *)dp + start; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4168 | if (idx == 0) // line is last in block |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4169 | old_len = dp->db_txt_end - start; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4170 | else // text of previous line follows |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4171 | old_len = (dp->db_index[idx - 1] & DB_INDEX_MASK) - start; |
Bram Moolenaar | 98aefe7 | 2018-12-13 22:20:09 +0100 | [diff] [blame] | 4172 | new_len = buf->b_ml.ml_line_len; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4173 | extra = new_len - old_len; // negative if lines gets smaller |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4174 | |
| 4175 | /* |
| 4176 | * if new line fits in data block, replace directly |
| 4177 | */ |
| 4178 | if ((int)dp->db_free >= extra) |
| 4179 | { |
Bram Moolenaar | 92755bb | 2021-08-15 22:18:04 +0200 | [diff] [blame] | 4180 | #if defined(FEAT_BYTEOFF) && defined(FEAT_PROP_POPUP) |
Bram Moolenaar | 14c7530 | 2021-08-15 14:28:40 +0200 | [diff] [blame] | 4181 | int old_prop_len = 0; |
Paul Ollis | 4c3d21a | 2022-05-24 21:26:37 +0100 | [diff] [blame] | 4182 | if (buf->b_has_textprop) |
| 4183 | old_prop_len = old_len - (int)STRLEN(old_line) - 1; |
Bram Moolenaar | 14c7530 | 2021-08-15 14:28:40 +0200 | [diff] [blame] | 4184 | #endif |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4185 | // if the length changes and there are following lines |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4186 | count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 1; |
| 4187 | if (extra != 0 && idx < count - 1) |
| 4188 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4189 | // move text of following lines |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4190 | mch_memmove((char *)dp + dp->db_txt_start - extra, |
| 4191 | (char *)dp + dp->db_txt_start, |
| 4192 | (size_t)(start - dp->db_txt_start)); |
| 4193 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4194 | // adjust pointers of this and following lines |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4195 | for (i = idx + 1; i < count; ++i) |
| 4196 | dp->db_index[i] -= extra; |
| 4197 | } |
| 4198 | dp->db_index[idx] -= extra; |
| 4199 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4200 | // adjust free space |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4201 | dp->db_free -= extra; |
| 4202 | dp->db_txt_start -= extra; |
| 4203 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4204 | // copy new line into the data block |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4205 | mch_memmove(old_line - extra, new_line, (size_t)new_len); |
| 4206 | buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS); |
Bram Moolenaar | 92755bb | 2021-08-15 22:18:04 +0200 | [diff] [blame] | 4207 | #if defined(FEAT_BYTEOFF) && defined(FEAT_PROP_POPUP) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4208 | // The else case is already covered by the insert and delete |
Bram Moolenaar | 14c7530 | 2021-08-15 14:28:40 +0200 | [diff] [blame] | 4209 | if (buf->b_has_textprop) |
| 4210 | { |
| 4211 | // Do not count the size of any text properties. |
| 4212 | extra += old_prop_len; |
Bram Moolenaar | 434df7a | 2021-08-16 21:15:32 +0200 | [diff] [blame] | 4213 | extra -= new_len - (int)STRLEN(new_line) - 1; |
Bram Moolenaar | 14c7530 | 2021-08-15 14:28:40 +0200 | [diff] [blame] | 4214 | } |
| 4215 | if (extra != 0) |
| 4216 | ml_updatechunk(buf, lnum, (long)extra, ML_CHNK_UPDLINE); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4217 | #endif |
| 4218 | } |
| 4219 | else |
| 4220 | { |
| 4221 | /* |
| 4222 | * Cannot do it in one data block: Delete and append. |
| 4223 | * Append first, because ml_delete_int() cannot delete the |
| 4224 | * last line in a buffer, which causes trouble for a buffer |
| 4225 | * that has only one line. |
| 4226 | * Don't forget to copy the mark! |
| 4227 | */ |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4228 | // How about handling errors??? |
Bram Moolenaar | a9d4b84 | 2020-05-30 14:46:52 +0200 | [diff] [blame] | 4229 | (void)ml_append_int(buf, lnum, new_line, new_len, |
Bram Moolenaar | 840f91f | 2021-05-26 22:32:10 +0200 | [diff] [blame] | 4230 | ((dp->db_index[idx] & DB_MARKED) ? ML_APPEND_MARK : 0) |
| 4231 | #ifdef FEAT_PROP_POPUP |
| 4232 | | ML_APPEND_NOPROP |
| 4233 | #endif |
| 4234 | ); |
Bram Moolenaar | 4cd5c52 | 2021-06-27 13:04:00 +0200 | [diff] [blame] | 4235 | (void)ml_delete_int(buf, lnum, ML_DEL_NOPROP); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4236 | } |
| 4237 | } |
| 4238 | vim_free(new_line); |
Bram Moolenaar | 0ca4b35 | 2010-02-11 18:54:43 +0100 | [diff] [blame] | 4239 | |
| 4240 | entered = FALSE; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4241 | } |
Bram Moolenaar | fa4873c | 2022-06-30 22:13:59 +0100 | [diff] [blame] | 4242 | else if (buf->b_ml.ml_flags & ML_ALLOCATED) |
| 4243 | vim_free(buf->b_ml.ml_line_ptr); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4244 | |
Bram Moolenaar | fa4873c | 2022-06-30 22:13:59 +0100 | [diff] [blame] | 4245 | buf->b_ml.ml_flags &= ~(ML_LINE_DIRTY | ML_ALLOCATED); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4246 | buf->b_ml.ml_line_lnum = 0; |
| 4247 | } |
| 4248 | |
| 4249 | /* |
| 4250 | * create a new, empty, data block |
| 4251 | */ |
| 4252 | static bhdr_T * |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 4253 | ml_new_data(memfile_T *mfp, int negative, int page_count) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4254 | { |
| 4255 | bhdr_T *hp; |
| 4256 | DATA_BL *dp; |
| 4257 | |
| 4258 | if ((hp = mf_new(mfp, negative, page_count)) == NULL) |
| 4259 | return NULL; |
| 4260 | |
| 4261 | dp = (DATA_BL *)(hp->bh_data); |
| 4262 | dp->db_id = DATA_ID; |
| 4263 | dp->db_txt_start = dp->db_txt_end = page_count * mfp->mf_page_size; |
| 4264 | dp->db_free = dp->db_txt_start - HEADER_SIZE; |
| 4265 | dp->db_line_count = 0; |
| 4266 | |
| 4267 | return hp; |
| 4268 | } |
| 4269 | |
| 4270 | /* |
| 4271 | * create a new, empty, pointer block |
| 4272 | */ |
| 4273 | static bhdr_T * |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 4274 | ml_new_ptr(memfile_T *mfp) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4275 | { |
| 4276 | bhdr_T *hp; |
| 4277 | PTR_BL *pp; |
| 4278 | |
| 4279 | if ((hp = mf_new(mfp, FALSE, 1)) == NULL) |
| 4280 | return NULL; |
| 4281 | |
| 4282 | pp = (PTR_BL *)(hp->bh_data); |
| 4283 | pp->pb_id = PTR_ID; |
| 4284 | pp->pb_count = 0; |
Bram Moolenaar | b67ba03 | 2023-04-22 21:14:26 +0100 | [diff] [blame] | 4285 | pp->pb_count_max = PB_COUNT_MAX(mfp); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4286 | |
| 4287 | return hp; |
| 4288 | } |
| 4289 | |
| 4290 | /* |
Bram Moolenaar | c1a9bc1 | 2018-12-28 21:59:29 +0100 | [diff] [blame] | 4291 | * Lookup line 'lnum' in a memline. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4292 | * |
| 4293 | * action: if ML_DELETE or ML_INSERT the line count is updated while searching |
| 4294 | * if ML_FLUSH only flush a locked block |
| 4295 | * if ML_FIND just find the line |
| 4296 | * |
| 4297 | * If the block was found it is locked and put in ml_locked. |
| 4298 | * The stack is updated to lead to the locked block. The ip_high field in |
| 4299 | * the stack is updated to reflect the last line in the block AFTER the |
| 4300 | * insert or delete, also if the pointer block has not been updated yet. But |
Bram Moolenaar | 6b803a7 | 2007-05-06 14:25:46 +0000 | [diff] [blame] | 4301 | * if ml_locked != NULL ml_locked_lineadd must be added to ip_high. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4302 | * |
| 4303 | * return: NULL for failure, pointer to block header otherwise |
| 4304 | */ |
| 4305 | static bhdr_T * |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 4306 | ml_find_line(buf_T *buf, linenr_T lnum, int action) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4307 | { |
| 4308 | DATA_BL *dp; |
| 4309 | PTR_BL *pp; |
| 4310 | infoptr_T *ip; |
| 4311 | bhdr_T *hp; |
| 4312 | memfile_T *mfp; |
| 4313 | linenr_T t; |
| 4314 | blocknr_T bnum, bnum2; |
| 4315 | int dirty; |
| 4316 | linenr_T low, high; |
| 4317 | int top; |
| 4318 | int page_count; |
| 4319 | int idx; |
| 4320 | |
| 4321 | mfp = buf->b_ml.ml_mfp; |
| 4322 | |
| 4323 | /* |
| 4324 | * If there is a locked block check if the wanted line is in it. |
| 4325 | * If not, flush and release the locked block. |
| 4326 | * Don't do this for ML_INSERT_SAME, because the stack need to be updated. |
| 4327 | * Don't do this for ML_FLUSH, because we want to flush the locked block. |
Bram Moolenaar | 47b8b15 | 2007-02-07 02:41:57 +0000 | [diff] [blame] | 4328 | * Don't do this when 'swapfile' is reset, we want to load all the blocks. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4329 | */ |
| 4330 | if (buf->b_ml.ml_locked) |
| 4331 | { |
Bram Moolenaar | 47b8b15 | 2007-02-07 02:41:57 +0000 | [diff] [blame] | 4332 | if (ML_SIMPLE(action) |
| 4333 | && buf->b_ml.ml_locked_low <= lnum |
| 4334 | && buf->b_ml.ml_locked_high >= lnum |
| 4335 | && !mf_dont_release) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4336 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4337 | // remember to update pointer blocks and stack later |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4338 | if (action == ML_INSERT) |
| 4339 | { |
| 4340 | ++(buf->b_ml.ml_locked_lineadd); |
| 4341 | ++(buf->b_ml.ml_locked_high); |
| 4342 | } |
| 4343 | else if (action == ML_DELETE) |
| 4344 | { |
| 4345 | --(buf->b_ml.ml_locked_lineadd); |
| 4346 | --(buf->b_ml.ml_locked_high); |
| 4347 | } |
| 4348 | return (buf->b_ml.ml_locked); |
| 4349 | } |
| 4350 | |
| 4351 | mf_put(mfp, buf->b_ml.ml_locked, buf->b_ml.ml_flags & ML_LOCKED_DIRTY, |
| 4352 | buf->b_ml.ml_flags & ML_LOCKED_POS); |
| 4353 | buf->b_ml.ml_locked = NULL; |
| 4354 | |
Bram Moolenaar | 6b803a7 | 2007-05-06 14:25:46 +0000 | [diff] [blame] | 4355 | /* |
| 4356 | * If lines have been added or deleted in the locked block, need to |
| 4357 | * update the line count in pointer blocks. |
| 4358 | */ |
| 4359 | if (buf->b_ml.ml_locked_lineadd != 0) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4360 | ml_lineadd(buf, buf->b_ml.ml_locked_lineadd); |
| 4361 | } |
| 4362 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4363 | if (action == ML_FLUSH) // nothing else to do |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4364 | return NULL; |
| 4365 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4366 | bnum = 1; // start at the root of the tree |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4367 | page_count = 1; |
| 4368 | low = 1; |
| 4369 | high = buf->b_ml.ml_line_count; |
| 4370 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4371 | if (action == ML_FIND) // first try stack entries |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4372 | { |
| 4373 | for (top = buf->b_ml.ml_stack_top - 1; top >= 0; --top) |
| 4374 | { |
| 4375 | ip = &(buf->b_ml.ml_stack[top]); |
| 4376 | if (ip->ip_low <= lnum && ip->ip_high >= lnum) |
| 4377 | { |
| 4378 | bnum = ip->ip_bnum; |
| 4379 | low = ip->ip_low; |
| 4380 | high = ip->ip_high; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4381 | buf->b_ml.ml_stack_top = top; // truncate stack at prev entry |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4382 | break; |
| 4383 | } |
| 4384 | } |
| 4385 | if (top < 0) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4386 | buf->b_ml.ml_stack_top = 0; // not found, start at the root |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4387 | } |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4388 | else // ML_DELETE or ML_INSERT |
| 4389 | buf->b_ml.ml_stack_top = 0; // start at the root |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4390 | |
| 4391 | /* |
| 4392 | * search downwards in the tree until a data block is found |
| 4393 | */ |
| 4394 | for (;;) |
| 4395 | { |
| 4396 | if ((hp = mf_get(mfp, bnum, page_count)) == NULL) |
| 4397 | goto error_noblock; |
| 4398 | |
| 4399 | /* |
| 4400 | * update high for insert/delete |
| 4401 | */ |
| 4402 | if (action == ML_INSERT) |
| 4403 | ++high; |
| 4404 | else if (action == ML_DELETE) |
| 4405 | --high; |
| 4406 | |
| 4407 | dp = (DATA_BL *)(hp->bh_data); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4408 | if (dp->db_id == DATA_ID) // data block |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4409 | { |
| 4410 | buf->b_ml.ml_locked = hp; |
| 4411 | buf->b_ml.ml_locked_low = low; |
| 4412 | buf->b_ml.ml_locked_high = high; |
| 4413 | buf->b_ml.ml_locked_lineadd = 0; |
| 4414 | buf->b_ml.ml_flags &= ~(ML_LOCKED_DIRTY | ML_LOCKED_POS); |
| 4415 | return hp; |
| 4416 | } |
| 4417 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4418 | pp = (PTR_BL *)(dp); // must be pointer block |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4419 | if (pp->pb_id != PTR_ID) |
| 4420 | { |
RestorerZ | 68ebcee | 2023-05-31 17:12:14 +0100 | [diff] [blame] | 4421 | iemsg(e_pointer_block_id_wrong); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4422 | goto error_block; |
| 4423 | } |
| 4424 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4425 | if ((top = ml_add_stack(buf)) < 0) // add new entry to stack |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4426 | goto error_block; |
| 4427 | ip = &(buf->b_ml.ml_stack[top]); |
| 4428 | ip->ip_bnum = bnum; |
| 4429 | ip->ip_low = low; |
| 4430 | ip->ip_high = high; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4431 | ip->ip_index = -1; // index not known yet |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4432 | |
| 4433 | dirty = FALSE; |
| 4434 | for (idx = 0; idx < (int)pp->pb_count; ++idx) |
| 4435 | { |
| 4436 | t = pp->pb_pointer[idx].pe_line_count; |
| 4437 | CHECK(t == 0, _("pe_line_count is zero")); |
| 4438 | if ((low += t) > lnum) |
| 4439 | { |
| 4440 | ip->ip_index = idx; |
| 4441 | bnum = pp->pb_pointer[idx].pe_bnum; |
| 4442 | page_count = pp->pb_pointer[idx].pe_page_count; |
| 4443 | high = low - 1; |
| 4444 | low -= t; |
| 4445 | |
| 4446 | /* |
| 4447 | * a negative block number may have been changed |
| 4448 | */ |
| 4449 | if (bnum < 0) |
| 4450 | { |
| 4451 | bnum2 = mf_trans_del(mfp, bnum); |
| 4452 | if (bnum != bnum2) |
| 4453 | { |
| 4454 | bnum = bnum2; |
| 4455 | pp->pb_pointer[idx].pe_bnum = bnum; |
| 4456 | dirty = TRUE; |
| 4457 | } |
| 4458 | } |
| 4459 | |
| 4460 | break; |
| 4461 | } |
| 4462 | } |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4463 | if (idx >= (int)pp->pb_count) // past the end: something wrong! |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4464 | { |
| 4465 | if (lnum > buf->b_ml.ml_line_count) |
RestorerZ | 68ebcee | 2023-05-31 17:12:14 +0100 | [diff] [blame] | 4466 | siemsg(e_line_number_out_of_range_nr_past_the_end, |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4467 | lnum - buf->b_ml.ml_line_count); |
| 4468 | |
| 4469 | else |
RestorerZ | 68ebcee | 2023-05-31 17:12:14 +0100 | [diff] [blame] | 4470 | siemsg(e_line_count_wrong_in_block_nr, bnum); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4471 | goto error_block; |
| 4472 | } |
| 4473 | if (action == ML_DELETE) |
| 4474 | { |
| 4475 | pp->pb_pointer[idx].pe_line_count--; |
| 4476 | dirty = TRUE; |
| 4477 | } |
| 4478 | else if (action == ML_INSERT) |
| 4479 | { |
| 4480 | pp->pb_pointer[idx].pe_line_count++; |
| 4481 | dirty = TRUE; |
| 4482 | } |
| 4483 | mf_put(mfp, hp, dirty, FALSE); |
| 4484 | } |
| 4485 | |
| 4486 | error_block: |
| 4487 | mf_put(mfp, hp, FALSE, FALSE); |
| 4488 | error_noblock: |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 4489 | /* |
| 4490 | * If action is ML_DELETE or ML_INSERT we have to correct the tree for |
| 4491 | * the incremented/decremented line counts, because there won't be a line |
| 4492 | * inserted/deleted after all. |
| 4493 | */ |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4494 | if (action == ML_DELETE) |
| 4495 | ml_lineadd(buf, 1); |
| 4496 | else if (action == ML_INSERT) |
| 4497 | ml_lineadd(buf, -1); |
| 4498 | buf->b_ml.ml_stack_top = 0; |
| 4499 | return NULL; |
| 4500 | } |
| 4501 | |
| 4502 | /* |
| 4503 | * add an entry to the info pointer stack |
| 4504 | * |
| 4505 | * return -1 for failure, number of the new entry otherwise |
| 4506 | */ |
| 4507 | static int |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 4508 | ml_add_stack(buf_T *buf) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4509 | { |
| 4510 | int top; |
| 4511 | infoptr_T *newstack; |
| 4512 | |
| 4513 | top = buf->b_ml.ml_stack_top; |
| 4514 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4515 | // may have to increase the stack size |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4516 | if (top == buf->b_ml.ml_stack_size) |
| 4517 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4518 | CHECK(top > 0, _("Stack size increases")); // more than 5 levels??? |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4519 | |
Bram Moolenaar | c799fe2 | 2019-05-28 23:08:19 +0200 | [diff] [blame] | 4520 | newstack = ALLOC_MULT(infoptr_T, buf->b_ml.ml_stack_size + STACK_INCR); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4521 | if (newstack == NULL) |
| 4522 | return -1; |
Bram Moolenaar | fbd302f | 2015-08-08 18:23:46 +0200 | [diff] [blame] | 4523 | if (top > 0) |
| 4524 | mch_memmove(newstack, buf->b_ml.ml_stack, |
Bram Moolenaar | 8c8de83 | 2008-06-24 22:58:06 +0000 | [diff] [blame] | 4525 | (size_t)top * sizeof(infoptr_T)); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4526 | vim_free(buf->b_ml.ml_stack); |
| 4527 | buf->b_ml.ml_stack = newstack; |
| 4528 | buf->b_ml.ml_stack_size += STACK_INCR; |
| 4529 | } |
| 4530 | |
| 4531 | buf->b_ml.ml_stack_top++; |
| 4532 | return top; |
| 4533 | } |
| 4534 | |
| 4535 | /* |
| 4536 | * Update the pointer blocks on the stack for inserted/deleted lines. |
| 4537 | * The stack itself is also updated. |
| 4538 | * |
| 4539 | * When a insert/delete line action fails, the line is not inserted/deleted, |
| 4540 | * but the pointer blocks have already been updated. That is fixed here by |
| 4541 | * walking through the stack. |
| 4542 | * |
| 4543 | * Count is the number of lines added, negative if lines have been deleted. |
| 4544 | */ |
| 4545 | static void |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 4546 | ml_lineadd(buf_T *buf, int count) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4547 | { |
| 4548 | int idx; |
| 4549 | infoptr_T *ip; |
| 4550 | PTR_BL *pp; |
| 4551 | memfile_T *mfp = buf->b_ml.ml_mfp; |
| 4552 | bhdr_T *hp; |
| 4553 | |
| 4554 | for (idx = buf->b_ml.ml_stack_top - 1; idx >= 0; --idx) |
| 4555 | { |
| 4556 | ip = &(buf->b_ml.ml_stack[idx]); |
| 4557 | if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL) |
| 4558 | break; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4559 | pp = (PTR_BL *)(hp->bh_data); // must be pointer block |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4560 | if (pp->pb_id != PTR_ID) |
| 4561 | { |
| 4562 | mf_put(mfp, hp, FALSE, FALSE); |
RestorerZ | 68ebcee | 2023-05-31 17:12:14 +0100 | [diff] [blame] | 4563 | iemsg(e_pointer_block_id_wrong_two); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4564 | break; |
| 4565 | } |
| 4566 | pp->pb_pointer[ip->ip_index].pe_line_count += count; |
| 4567 | ip->ip_high += count; |
| 4568 | mf_put(mfp, hp, TRUE, FALSE); |
| 4569 | } |
| 4570 | } |
| 4571 | |
Bram Moolenaar | 55debbe | 2010-05-23 23:34:36 +0200 | [diff] [blame] | 4572 | #if defined(HAVE_READLINK) || defined(PROTO) |
Bram Moolenaar | 900b4d7 | 2005-12-12 22:05:50 +0000 | [diff] [blame] | 4573 | /* |
| 4574 | * Resolve a symlink in the last component of a file name. |
| 4575 | * Note that f_resolve() does it for every part of the path, we don't do that |
| 4576 | * here. |
| 4577 | * If it worked returns OK and the resolved link in "buf[MAXPATHL]". |
| 4578 | * Otherwise returns FAIL. |
| 4579 | */ |
Bram Moolenaar | 55debbe | 2010-05-23 23:34:36 +0200 | [diff] [blame] | 4580 | int |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 4581 | resolve_symlink(char_u *fname, char_u *buf) |
Bram Moolenaar | 900b4d7 | 2005-12-12 22:05:50 +0000 | [diff] [blame] | 4582 | { |
| 4583 | char_u tmp[MAXPATHL]; |
| 4584 | int ret; |
| 4585 | int depth = 0; |
| 4586 | |
| 4587 | if (fname == NULL) |
| 4588 | return FAIL; |
| 4589 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4590 | // Put the result so far in tmp[], starting with the original name. |
Bram Moolenaar | 900b4d7 | 2005-12-12 22:05:50 +0000 | [diff] [blame] | 4591 | vim_strncpy(tmp, fname, MAXPATHL - 1); |
| 4592 | |
| 4593 | for (;;) |
| 4594 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4595 | // Limit symlink depth to 100, catch recursive loops. |
Bram Moolenaar | 900b4d7 | 2005-12-12 22:05:50 +0000 | [diff] [blame] | 4596 | if (++depth == 100) |
| 4597 | { |
Bram Moolenaar | 677658a | 2022-01-05 16:09:06 +0000 | [diff] [blame] | 4598 | semsg(_(e_symlink_loop_for_str), fname); |
Bram Moolenaar | 900b4d7 | 2005-12-12 22:05:50 +0000 | [diff] [blame] | 4599 | return FAIL; |
| 4600 | } |
| 4601 | |
| 4602 | ret = readlink((char *)tmp, (char *)buf, MAXPATHL - 1); |
| 4603 | if (ret <= 0) |
| 4604 | { |
Bram Moolenaar | cc98426 | 2005-12-23 22:19:46 +0000 | [diff] [blame] | 4605 | if (errno == EINVAL || errno == ENOENT) |
Bram Moolenaar | 900b4d7 | 2005-12-12 22:05:50 +0000 | [diff] [blame] | 4606 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4607 | // Found non-symlink or not existing file, stop here. |
| 4608 | // When at the first level use the unmodified name, skip the |
| 4609 | // call to vim_FullName(). |
Bram Moolenaar | 900b4d7 | 2005-12-12 22:05:50 +0000 | [diff] [blame] | 4610 | if (depth == 1) |
| 4611 | return FAIL; |
| 4612 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4613 | // Use the resolved name in tmp[]. |
Bram Moolenaar | 900b4d7 | 2005-12-12 22:05:50 +0000 | [diff] [blame] | 4614 | break; |
| 4615 | } |
| 4616 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4617 | // There must be some error reading links, use original name. |
Bram Moolenaar | 900b4d7 | 2005-12-12 22:05:50 +0000 | [diff] [blame] | 4618 | return FAIL; |
| 4619 | } |
| 4620 | buf[ret] = NUL; |
| 4621 | |
| 4622 | /* |
| 4623 | * Check whether the symlink is relative or absolute. |
| 4624 | * If it's relative, build a new path based on the directory |
| 4625 | * portion of the filename (if any) and the path the symlink |
| 4626 | * points to. |
| 4627 | */ |
| 4628 | if (mch_isFullName(buf)) |
| 4629 | STRCPY(tmp, buf); |
| 4630 | else |
| 4631 | { |
| 4632 | char_u *tail; |
| 4633 | |
| 4634 | tail = gettail(tmp); |
| 4635 | if (STRLEN(tail) + STRLEN(buf) >= MAXPATHL) |
| 4636 | return FAIL; |
| 4637 | STRCPY(tail, buf); |
| 4638 | } |
| 4639 | } |
| 4640 | |
| 4641 | /* |
| 4642 | * Try to resolve the full name of the file so that the swapfile name will |
| 4643 | * be consistent even when opening a relative symlink from different |
| 4644 | * working directories. |
| 4645 | */ |
| 4646 | return vim_FullName(tmp, buf, MAXPATHL, TRUE); |
| 4647 | } |
| 4648 | #endif |
| 4649 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4650 | /* |
Bram Moolenaar | 04a09c1 | 2005-08-01 22:02:32 +0000 | [diff] [blame] | 4651 | * Make swap file name out of the file name and a directory name. |
| 4652 | * Returns pointer to allocated memory or NULL. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4653 | */ |
Bram Moolenaar | 04a09c1 | 2005-08-01 22:02:32 +0000 | [diff] [blame] | 4654 | char_u * |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 4655 | makeswapname( |
| 4656 | char_u *fname, |
| 4657 | char_u *ffname UNUSED, |
| 4658 | buf_T *buf, |
| 4659 | char_u *dir_name) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4660 | { |
| 4661 | char_u *r, *s; |
Bram Moolenaar | 9dbe475 | 2010-05-14 17:52:42 +0200 | [diff] [blame] | 4662 | char_u *fname_res = fname; |
Bram Moolenaar | 900b4d7 | 2005-12-12 22:05:50 +0000 | [diff] [blame] | 4663 | #ifdef HAVE_READLINK |
| 4664 | char_u fname_buf[MAXPATHL]; |
Bram Moolenaar | 5966ea1 | 2020-07-15 15:30:05 +0200 | [diff] [blame] | 4665 | |
| 4666 | // Expand symlink in the file name, so that we put the swap file with the |
| 4667 | // actual file instead of with the symlink. |
| 4668 | if (resolve_symlink(fname, fname_buf) == OK) |
| 4669 | fname_res = fname_buf; |
Bram Moolenaar | 900b4d7 | 2005-12-12 22:05:50 +0000 | [diff] [blame] | 4670 | #endif |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4671 | |
Bram Moolenaar | 4f97475 | 2019-02-17 17:44:42 +0100 | [diff] [blame] | 4672 | #if defined(UNIX) || defined(MSWIN) // Need _very_ long file names |
Bram Moolenaar | b113c3a | 2017-02-28 21:26:17 +0100 | [diff] [blame] | 4673 | int len = (int)STRLEN(dir_name); |
Bram Moolenaar | c525e3a | 2017-02-18 16:59:02 +0100 | [diff] [blame] | 4674 | |
| 4675 | s = dir_name + len; |
| 4676 | if (after_pathsep(dir_name, s) && len > 1 && s[-1] == s[-2]) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4677 | { // Ends with '//', Use Full path |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4678 | r = NULL; |
zeertzjq | 242667a | 2024-07-25 20:58:42 +0200 | [diff] [blame] | 4679 | if ((s = make_percent_swname(dir_name, s, fname_res)) != NULL) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4680 | { |
| 4681 | r = modname(s, (char_u *)".swp", FALSE); |
| 4682 | vim_free(s); |
| 4683 | } |
| 4684 | return r; |
| 4685 | } |
| 4686 | #endif |
| 4687 | |
| 4688 | r = buf_modname( |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4689 | (buf->b_p_sn || buf->b_shortname), |
Bram Moolenaar | 900b4d7 | 2005-12-12 22:05:50 +0000 | [diff] [blame] | 4690 | fname_res, |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4691 | (char_u *) |
Bram Moolenaar | e60acc1 | 2011-05-10 16:41:25 +0200 | [diff] [blame] | 4692 | #if defined(VMS) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4693 | "_swp", |
| 4694 | #else |
| 4695 | ".swp", |
| 4696 | #endif |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4697 | // Prepend a '.' to the swap file name for the current directory. |
Bram Moolenaar | 48e330a | 2016-02-23 14:53:34 +0100 | [diff] [blame] | 4698 | dir_name[0] == '.' && dir_name[1] == NUL); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4699 | if (r == NULL) // out of memory |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4700 | return NULL; |
| 4701 | |
| 4702 | s = get_file_in_dir(r, dir_name); |
| 4703 | vim_free(r); |
| 4704 | return s; |
| 4705 | } |
| 4706 | |
| 4707 | /* |
| 4708 | * Get file name to use for swap file or backup file. |
| 4709 | * Use the name of the edited file "fname" and an entry in the 'dir' or 'bdir' |
| 4710 | * option "dname". |
| 4711 | * - If "dname" is ".", return "fname" (swap file in dir of file). |
| 4712 | * - If "dname" starts with "./", insert "dname" in "fname" (swap file |
| 4713 | * relative to dir of file). |
| 4714 | * - Otherwise, prepend "dname" to the tail of "fname" (swap file in specific |
| 4715 | * dir). |
| 4716 | * |
| 4717 | * The return value is an allocated string and can be NULL. |
| 4718 | */ |
| 4719 | char_u * |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 4720 | get_file_in_dir( |
| 4721 | char_u *fname, |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4722 | char_u *dname) // don't use "dirname", it is a global for Alpha |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4723 | { |
| 4724 | char_u *t; |
| 4725 | char_u *tail; |
| 4726 | char_u *retval; |
| 4727 | int save_char; |
| 4728 | |
| 4729 | tail = gettail(fname); |
| 4730 | |
| 4731 | if (dname[0] == '.' && dname[1] == NUL) |
| 4732 | retval = vim_strsave(fname); |
| 4733 | else if (dname[0] == '.' && vim_ispathsep(dname[1])) |
| 4734 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4735 | if (tail == fname) // no path before file name |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4736 | retval = concat_fnames(dname + 2, tail, TRUE); |
| 4737 | else |
| 4738 | { |
| 4739 | save_char = *tail; |
| 4740 | *tail = NUL; |
| 4741 | t = concat_fnames(fname, dname + 2, TRUE); |
| 4742 | *tail = save_char; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4743 | if (t == NULL) // out of memory |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4744 | retval = NULL; |
| 4745 | else |
| 4746 | { |
| 4747 | retval = concat_fnames(t, tail, TRUE); |
| 4748 | vim_free(t); |
| 4749 | } |
| 4750 | } |
| 4751 | } |
| 4752 | else |
| 4753 | retval = concat_fnames(dname, tail, TRUE); |
| 4754 | |
Bram Moolenaar | 4f97475 | 2019-02-17 17:44:42 +0100 | [diff] [blame] | 4755 | #ifdef MSWIN |
Bram Moolenaar | 69c3500 | 2013-11-04 02:54:12 +0100 | [diff] [blame] | 4756 | if (retval != NULL) |
Bram Moolenaar | 91acfff | 2017-03-12 19:22:36 +0100 | [diff] [blame] | 4757 | for (t = gettail(retval); *t != NUL; MB_PTR_ADV(t)) |
Bram Moolenaar | 69c3500 | 2013-11-04 02:54:12 +0100 | [diff] [blame] | 4758 | if (*t == ':') |
| 4759 | *t = '%'; |
| 4760 | #endif |
| 4761 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4762 | return retval; |
| 4763 | } |
| 4764 | |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 4765 | /* |
| 4766 | * Print the ATTENTION message: info about an existing swap file. |
| 4767 | */ |
| 4768 | static void |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 4769 | attention_message( |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4770 | buf_T *buf, // buffer being edited |
| 4771 | char_u *fname) // swap file name |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 4772 | { |
Bram Moolenaar | 8767f52 | 2016-07-01 17:17:39 +0200 | [diff] [blame] | 4773 | stat_T st; |
Bram Moolenaar | 63d2555 | 2019-05-10 21:28:38 +0200 | [diff] [blame] | 4774 | time_t swap_mtime; |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 4775 | |
| 4776 | ++no_wait_return; |
Bram Moolenaar | eaaac01 | 2022-01-02 17:00:40 +0000 | [diff] [blame] | 4777 | (void)emsg(_(e_attention)); |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 4778 | msg_puts(_("\nFound a swap file by the name \"")); |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 4779 | msg_home_replace(fname); |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 4780 | msg_puts("\"\n"); |
Bram Moolenaar | 63d2555 | 2019-05-10 21:28:38 +0200 | [diff] [blame] | 4781 | swap_mtime = swapfile_info(fname); |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 4782 | msg_puts(_("While opening file \"")); |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 4783 | msg_outtrans(buf->b_fname); |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 4784 | msg_puts("\"\n"); |
Bram Moolenaar | d6105cb | 2018-10-13 19:06:27 +0200 | [diff] [blame] | 4785 | if (mch_stat((char *)buf->b_fname, &st) == -1) |
| 4786 | { |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 4787 | msg_puts(_(" CANNOT BE FOUND")); |
Bram Moolenaar | d6105cb | 2018-10-13 19:06:27 +0200 | [diff] [blame] | 4788 | } |
| 4789 | else |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 4790 | { |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 4791 | msg_puts(_(" dated: ")); |
Bram Moolenaar | 63d2555 | 2019-05-10 21:28:38 +0200 | [diff] [blame] | 4792 | msg_puts(get_ctime(st.st_mtime, TRUE)); |
| 4793 | if (swap_mtime != 0 && st.st_mtime > swap_mtime) |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 4794 | msg_puts(_(" NEWER than swap file!\n")); |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 4795 | } |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4796 | // Some of these messages are long to allow translation to |
| 4797 | // other languages. |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 4798 | 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")); |
| 4799 | msg_puts(_("(2) An edit session for this file crashed.\n")); |
| 4800 | msg_puts(_(" If this is the case, use \":recover\" or \"vim -r ")); |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 4801 | msg_outtrans(buf->b_fname); |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 4802 | msg_puts(_("\"\n to recover the changes (see \":help recovery\").\n")); |
| 4803 | msg_puts(_(" If you did this already, delete the swap file \"")); |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 4804 | msg_outtrans(fname); |
Bram Moolenaar | 32526b3 | 2019-01-19 17:43:09 +0100 | [diff] [blame] | 4805 | msg_puts(_("\"\n to avoid this message.\n")); |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 4806 | cmdline_row = msg_row; |
| 4807 | --no_wait_return; |
| 4808 | } |
| 4809 | |
Bram Moolenaar | 188639d | 2022-04-04 16:57:21 +0100 | [diff] [blame] | 4810 | typedef enum { |
| 4811 | SEA_CHOICE_NONE = 0, |
| 4812 | SEA_CHOICE_READONLY = 1, |
| 4813 | SEA_CHOICE_EDIT = 2, |
| 4814 | SEA_CHOICE_RECOVER = 3, |
| 4815 | SEA_CHOICE_DELETE = 4, |
| 4816 | SEA_CHOICE_QUIT = 5, |
| 4817 | SEA_CHOICE_ABORT = 6 |
| 4818 | } sea_choice_T; |
| 4819 | |
Bram Moolenaar | f2bd8ef | 2018-03-04 18:08:14 +0100 | [diff] [blame] | 4820 | #if defined(FEAT_EVAL) |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 4821 | /* |
| 4822 | * Trigger the SwapExists autocommands. |
Bram Moolenaar | 188639d | 2022-04-04 16:57:21 +0100 | [diff] [blame] | 4823 | * Returns a value for equivalent to do_dialog(). |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 4824 | */ |
Bram Moolenaar | 188639d | 2022-04-04 16:57:21 +0100 | [diff] [blame] | 4825 | static sea_choice_T |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 4826 | do_swapexists(buf_T *buf, char_u *fname) |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 4827 | { |
| 4828 | set_vim_var_string(VV_SWAPNAME, fname, -1); |
| 4829 | set_vim_var_string(VV_SWAPCHOICE, NULL, -1); |
| 4830 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4831 | // Trigger SwapExists autocommands with <afile> set to the file being |
| 4832 | // edited. Disallow changing directory here. |
Bram Moolenaar | 12c22ce | 2009-04-22 13:58:46 +0000 | [diff] [blame] | 4833 | ++allbuf_lock; |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 4834 | apply_autocmds(EVENT_SWAPEXISTS, buf->b_fname, NULL, FALSE, NULL); |
Bram Moolenaar | 12c22ce | 2009-04-22 13:58:46 +0000 | [diff] [blame] | 4835 | --allbuf_lock; |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 4836 | |
| 4837 | set_vim_var_string(VV_SWAPNAME, NULL, -1); |
| 4838 | |
| 4839 | switch (*get_vim_var_str(VV_SWAPCHOICE)) |
| 4840 | { |
Bram Moolenaar | 188639d | 2022-04-04 16:57:21 +0100 | [diff] [blame] | 4841 | case 'o': return SEA_CHOICE_READONLY; |
| 4842 | case 'e': return SEA_CHOICE_EDIT; |
| 4843 | case 'r': return SEA_CHOICE_RECOVER; |
| 4844 | case 'd': return SEA_CHOICE_DELETE; |
| 4845 | case 'q': return SEA_CHOICE_QUIT; |
| 4846 | case 'a': return SEA_CHOICE_ABORT; |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 4847 | } |
| 4848 | |
Bram Moolenaar | 188639d | 2022-04-04 16:57:21 +0100 | [diff] [blame] | 4849 | return SEA_CHOICE_NONE; |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 4850 | } |
| 4851 | #endif |
| 4852 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4853 | /* |
| 4854 | * Find out what name to use for the swap file for buffer 'buf'. |
| 4855 | * |
| 4856 | * Several names are tried to find one that does not exist |
Bram Moolenaar | 04a09c1 | 2005-08-01 22:02:32 +0000 | [diff] [blame] | 4857 | * Returns the name in allocated memory or NULL. |
Bram Moolenaar | f541c36 | 2011-10-26 11:44:18 +0200 | [diff] [blame] | 4858 | * When out of memory "dirp" is set to NULL. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4859 | * |
| 4860 | * Note: If BASENAMELEN is not correct, you will get error messages for |
Bram Moolenaar | 55debbe | 2010-05-23 23:34:36 +0200 | [diff] [blame] | 4861 | * not being able to open the swap or undo file |
Bram Moolenaar | 12c22ce | 2009-04-22 13:58:46 +0000 | [diff] [blame] | 4862 | * Note: May trigger SwapExists autocmd, pointers may change! |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4863 | */ |
| 4864 | static char_u * |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 4865 | findswapname( |
| 4866 | buf_T *buf, |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4867 | char_u **dirp, // pointer to list of directories |
| 4868 | char_u *old_fname) // don't give warning for this file name |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4869 | { |
| 4870 | char_u *fname; |
| 4871 | int n; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4872 | char_u *dir_name; |
| 4873 | #ifdef AMIGA |
| 4874 | BPTR fh; |
| 4875 | #endif |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4876 | int r; |
Bram Moolenaar | 69c3500 | 2013-11-04 02:54:12 +0100 | [diff] [blame] | 4877 | char_u *buf_fname = buf->b_fname; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4878 | |
Bram Moolenaar | 48e330a | 2016-02-23 14:53:34 +0100 | [diff] [blame] | 4879 | #if !defined(UNIX) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4880 | # define CREATE_DUMMY_FILE |
| 4881 | FILE *dummyfd = NULL; |
| 4882 | |
Bram Moolenaar | 4f97475 | 2019-02-17 17:44:42 +0100 | [diff] [blame] | 4883 | # ifdef MSWIN |
Bram Moolenaar | 69c3500 | 2013-11-04 02:54:12 +0100 | [diff] [blame] | 4884 | if (buf_fname != NULL && !mch_isFullName(buf_fname) |
| 4885 | && vim_strchr(gettail(buf_fname), ':')) |
| 4886 | { |
| 4887 | char_u *t; |
| 4888 | |
| 4889 | buf_fname = vim_strsave(buf_fname); |
| 4890 | if (buf_fname == NULL) |
| 4891 | buf_fname = buf->b_fname; |
| 4892 | else |
Bram Moolenaar | 91acfff | 2017-03-12 19:22:36 +0100 | [diff] [blame] | 4893 | for (t = gettail(buf_fname); *t != NUL; MB_PTR_ADV(t)) |
Bram Moolenaar | 69c3500 | 2013-11-04 02:54:12 +0100 | [diff] [blame] | 4894 | if (*t == ':') |
| 4895 | *t = '%'; |
| 4896 | } |
| 4897 | # endif |
| 4898 | |
Bram Moolenaar | 55debbe | 2010-05-23 23:34:36 +0200 | [diff] [blame] | 4899 | /* |
| 4900 | * If we start editing a new file, e.g. "test.doc", which resides on an |
| 4901 | * MSDOS compatible filesystem, it is possible that the file |
| 4902 | * "test.doc.swp" which we create will be exactly the same file. To avoid |
| 4903 | * this problem we temporarily create "test.doc". Don't do this when the |
Dominique Pelle | af4a61a | 2021-12-27 17:21:41 +0000 | [diff] [blame] | 4904 | * check below for an 8.3 file name is used. |
Bram Moolenaar | 55debbe | 2010-05-23 23:34:36 +0200 | [diff] [blame] | 4905 | */ |
Bram Moolenaar | 69c3500 | 2013-11-04 02:54:12 +0100 | [diff] [blame] | 4906 | if (!(buf->b_p_sn || buf->b_shortname) && buf_fname != NULL |
| 4907 | && mch_getperm(buf_fname) < 0) |
| 4908 | dummyfd = mch_fopen((char *)buf_fname, "w"); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4909 | #endif |
| 4910 | |
Bram Moolenaar | 55debbe | 2010-05-23 23:34:36 +0200 | [diff] [blame] | 4911 | /* |
| 4912 | * Isolate a directory name from *dirp and put it in dir_name. |
| 4913 | * First allocate some memory to put the directory name in. |
| 4914 | */ |
Bram Moolenaar | 964b374 | 2019-05-24 18:54:09 +0200 | [diff] [blame] | 4915 | dir_name = alloc(STRLEN(*dirp) + 1); |
Bram Moolenaar | f541c36 | 2011-10-26 11:44:18 +0200 | [diff] [blame] | 4916 | if (dir_name == NULL) |
| 4917 | *dirp = NULL; |
| 4918 | else |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4919 | (void)copy_option_part(dirp, dir_name, 31000, ","); |
| 4920 | |
Bram Moolenaar | 55debbe | 2010-05-23 23:34:36 +0200 | [diff] [blame] | 4921 | /* |
| 4922 | * we try different names until we find one that does not exist yet |
| 4923 | */ |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4924 | if (dir_name == NULL) // out of memory |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4925 | fname = NULL; |
| 4926 | else |
Bram Moolenaar | 69c3500 | 2013-11-04 02:54:12 +0100 | [diff] [blame] | 4927 | fname = makeswapname(buf_fname, buf->b_ffname, buf, dir_name); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4928 | |
| 4929 | for (;;) |
| 4930 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4931 | if (fname == NULL) // must be out of memory |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4932 | break; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4933 | if ((n = (int)STRLEN(fname)) == 0) // safety check |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4934 | { |
Bram Moolenaar | d23a823 | 2018-02-10 18:45:26 +0100 | [diff] [blame] | 4935 | VIM_CLEAR(fname); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4936 | break; |
| 4937 | } |
Bram Moolenaar | 48e330a | 2016-02-23 14:53:34 +0100 | [diff] [blame] | 4938 | #if defined(UNIX) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4939 | /* |
| 4940 | * Some systems have a MS-DOS compatible filesystem that use 8.3 character |
| 4941 | * file names. If this is the first try and the swap file name does not fit in |
| 4942 | * 8.3, detect if this is the case, set shortname and try again. |
| 4943 | */ |
| 4944 | if (fname[n - 2] == 'w' && fname[n - 1] == 'p' |
| 4945 | && !(buf->b_p_sn || buf->b_shortname)) |
| 4946 | { |
| 4947 | char_u *tail; |
| 4948 | char_u *fname2; |
Bram Moolenaar | 8767f52 | 2016-07-01 17:17:39 +0200 | [diff] [blame] | 4949 | stat_T s1, s2; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4950 | int f1, f2; |
| 4951 | int created1 = FALSE, created2 = FALSE; |
| 4952 | int same = FALSE; |
| 4953 | |
| 4954 | /* |
| 4955 | * Check if swapfile name does not fit in 8.3: |
| 4956 | * It either contains two dots, is longer than 8 chars, or starts |
| 4957 | * with a dot. |
| 4958 | */ |
Bram Moolenaar | 69c3500 | 2013-11-04 02:54:12 +0100 | [diff] [blame] | 4959 | tail = gettail(buf_fname); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4960 | if ( vim_strchr(tail, '.') != NULL |
| 4961 | || STRLEN(tail) > (size_t)8 |
| 4962 | || *gettail(fname) == '.') |
| 4963 | { |
| 4964 | fname2 = alloc(n + 2); |
| 4965 | if (fname2 != NULL) |
| 4966 | { |
| 4967 | STRCPY(fname2, fname); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 4968 | // if fname == "xx.xx.swp", fname2 = "xx.xx.swx" |
| 4969 | // if fname == ".xx.swp", fname2 = ".xx.swpx" |
| 4970 | // if fname == "123456789.swp", fname2 = "12345678x.swp" |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4971 | if (vim_strchr(tail, '.') != NULL) |
| 4972 | fname2[n - 1] = 'x'; |
| 4973 | else if (*gettail(fname) == '.') |
| 4974 | { |
| 4975 | fname2[n] = 'x'; |
| 4976 | fname2[n + 1] = NUL; |
| 4977 | } |
| 4978 | else |
| 4979 | fname2[n - 5] += 1; |
| 4980 | /* |
| 4981 | * may need to create the files to be able to use mch_stat() |
| 4982 | */ |
| 4983 | f1 = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0); |
| 4984 | if (f1 < 0) |
| 4985 | { |
| 4986 | f1 = mch_open_rw((char *)fname, |
| 4987 | O_RDWR|O_CREAT|O_EXCL|O_EXTRA); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 4988 | created1 = TRUE; |
| 4989 | } |
| 4990 | if (f1 >= 0) |
| 4991 | { |
| 4992 | f2 = mch_open((char *)fname2, O_RDONLY | O_EXTRA, 0); |
| 4993 | if (f2 < 0) |
| 4994 | { |
| 4995 | f2 = mch_open_rw((char *)fname2, |
| 4996 | O_RDWR|O_CREAT|O_EXCL|O_EXTRA); |
| 4997 | created2 = TRUE; |
| 4998 | } |
| 4999 | if (f2 >= 0) |
| 5000 | { |
| 5001 | /* |
| 5002 | * Both files exist now. If mch_stat() returns the |
| 5003 | * same device and inode they are the same file. |
| 5004 | */ |
| 5005 | if (mch_fstat(f1, &s1) != -1 |
| 5006 | && mch_fstat(f2, &s2) != -1 |
| 5007 | && s1.st_dev == s2.st_dev |
| 5008 | && s1.st_ino == s2.st_ino) |
| 5009 | same = TRUE; |
| 5010 | close(f2); |
| 5011 | if (created2) |
| 5012 | mch_remove(fname2); |
| 5013 | } |
| 5014 | close(f1); |
| 5015 | if (created1) |
| 5016 | mch_remove(fname); |
| 5017 | } |
| 5018 | vim_free(fname2); |
| 5019 | if (same) |
| 5020 | { |
| 5021 | buf->b_shortname = TRUE; |
| 5022 | vim_free(fname); |
Bram Moolenaar | 69c3500 | 2013-11-04 02:54:12 +0100 | [diff] [blame] | 5023 | fname = makeswapname(buf_fname, buf->b_ffname, |
Bram Moolenaar | 04a09c1 | 2005-08-01 22:02:32 +0000 | [diff] [blame] | 5024 | buf, dir_name); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5025 | continue; // try again with b_shortname set |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5026 | } |
| 5027 | } |
| 5028 | } |
| 5029 | } |
| 5030 | #endif |
| 5031 | /* |
| 5032 | * check if the swapfile already exists |
| 5033 | */ |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5034 | if (mch_getperm(fname) < 0) // it does not exist |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5035 | { |
| 5036 | #ifdef HAVE_LSTAT |
Bram Moolenaar | 8767f52 | 2016-07-01 17:17:39 +0200 | [diff] [blame] | 5037 | stat_T sb; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5038 | |
| 5039 | /* |
| 5040 | * Extra security check: When a swap file is a symbolic link, this |
| 5041 | * is most likely a symlink attack. |
| 5042 | */ |
| 5043 | if (mch_lstat((char *)fname, &sb) < 0) |
| 5044 | #else |
| 5045 | # ifdef AMIGA |
| 5046 | fh = Open((UBYTE *)fname, (long)MODE_NEWFILE); |
| 5047 | /* |
| 5048 | * on the Amiga mch_getperm() will return -1 when the file exists |
| 5049 | * but is being used by another program. This happens if you edit |
| 5050 | * a file twice. |
| 5051 | */ |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5052 | if (fh != (BPTR)NULL) // can open file, OK |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5053 | { |
| 5054 | Close(fh); |
| 5055 | mch_remove(fname); |
| 5056 | break; |
| 5057 | } |
| 5058 | if (IoErr() != ERROR_OBJECT_IN_USE |
| 5059 | && IoErr() != ERROR_OBJECT_EXISTS) |
| 5060 | # endif |
| 5061 | #endif |
| 5062 | break; |
| 5063 | } |
| 5064 | |
| 5065 | /* |
| 5066 | * A file name equal to old_fname is OK to use. |
| 5067 | */ |
| 5068 | if (old_fname != NULL && fnamecmp(fname, old_fname) == 0) |
| 5069 | break; |
| 5070 | |
| 5071 | /* |
| 5072 | * get here when file already exists |
| 5073 | */ |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5074 | if (fname[n - 2] == 'w' && fname[n - 1] == 'p') // first try |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5075 | { |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5076 | /* |
| 5077 | * on MS-DOS compatible filesystems (e.g. messydos) file.doc.swp |
| 5078 | * and file.doc are the same file. To guess if this problem is |
| 5079 | * present try if file.doc.swx exists. If it does, we set |
| 5080 | * buf->b_shortname and try file_doc.swp (dots replaced by |
| 5081 | * underscores for this file), and try again. If it doesn't we |
| 5082 | * assume that "file.doc.swp" already exists. |
| 5083 | */ |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5084 | if (!(buf->b_p_sn || buf->b_shortname)) // not tried yet |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5085 | { |
| 5086 | fname[n - 1] = 'x'; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5087 | r = mch_getperm(fname); // try "file.swx" |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5088 | fname[n - 1] = 'p'; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5089 | if (r >= 0) // "file.swx" seems to exist |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5090 | { |
| 5091 | buf->b_shortname = TRUE; |
| 5092 | vim_free(fname); |
Bram Moolenaar | 69c3500 | 2013-11-04 02:54:12 +0100 | [diff] [blame] | 5093 | fname = makeswapname(buf_fname, buf->b_ffname, |
Bram Moolenaar | 04a09c1 | 2005-08-01 22:02:32 +0000 | [diff] [blame] | 5094 | buf, dir_name); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5095 | continue; // try again with '.' replaced with '_' |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5096 | } |
| 5097 | } |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5098 | /* |
| 5099 | * If we get here the ".swp" file really exists. |
| 5100 | * Give an error message, unless recovering, no file name, we are |
| 5101 | * viewing a help file or when the path of the file is different |
| 5102 | * (happens when all .swp files are in one directory). |
| 5103 | */ |
Bram Moolenaar | 69c3500 | 2013-11-04 02:54:12 +0100 | [diff] [blame] | 5104 | if (!recoverymode && buf_fname != NULL |
Bram Moolenaar | 2debf1c | 2019-08-04 20:44:19 +0200 | [diff] [blame] | 5105 | && !buf->b_help |
| 5106 | && !(buf->b_flags & (BF_DUMMY | BF_NO_SEA))) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5107 | { |
| 5108 | int fd; |
| 5109 | struct block0 b0; |
| 5110 | int differ = FALSE; |
| 5111 | |
| 5112 | /* |
| 5113 | * Try to read block 0 from the swap file to get the original |
| 5114 | * file name (and inode number). |
| 5115 | */ |
| 5116 | fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0); |
| 5117 | if (fd >= 0) |
| 5118 | { |
Bram Moolenaar | 540fc6f | 2010-12-17 16:27:16 +0100 | [diff] [blame] | 5119 | if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0)) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5120 | { |
| 5121 | /* |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 5122 | * If the swapfile has the same directory as the |
| 5123 | * buffer don't compare the directory names, they can |
| 5124 | * have a different mountpoint. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5125 | */ |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 5126 | if (b0.b0_flags & B0_SAME_DIR) |
| 5127 | { |
| 5128 | if (fnamecmp(gettail(buf->b_ffname), |
| 5129 | gettail(b0.b0_fname)) != 0 |
| 5130 | || !same_directory(fname, buf->b_ffname)) |
Bram Moolenaar | 900b4d7 | 2005-12-12 22:05:50 +0000 | [diff] [blame] | 5131 | { |
| 5132 | #ifdef CHECK_INODE |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5133 | // Symlinks may point to the same file even |
| 5134 | // when the name differs, need to check the |
| 5135 | // inode too. |
Bram Moolenaar | 900b4d7 | 2005-12-12 22:05:50 +0000 | [diff] [blame] | 5136 | expand_env(b0.b0_fname, NameBuff, MAXPATHL); |
| 5137 | if (fnamecmp_ino(buf->b_ffname, NameBuff, |
| 5138 | char_to_long(b0.b0_ino))) |
| 5139 | #endif |
| 5140 | differ = TRUE; |
| 5141 | } |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 5142 | } |
| 5143 | else |
| 5144 | { |
| 5145 | /* |
| 5146 | * The name in the swap file may be |
| 5147 | * "~user/path/file". Expand it first. |
| 5148 | */ |
| 5149 | expand_env(b0.b0_fname, NameBuff, MAXPATHL); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5150 | #ifdef CHECK_INODE |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 5151 | if (fnamecmp_ino(buf->b_ffname, NameBuff, |
Bram Moolenaar | 900b4d7 | 2005-12-12 22:05:50 +0000 | [diff] [blame] | 5152 | char_to_long(b0.b0_ino))) |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 5153 | differ = TRUE; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5154 | #else |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 5155 | if (fnamecmp(NameBuff, buf->b_ffname) != 0) |
| 5156 | differ = TRUE; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5157 | #endif |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 5158 | } |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5159 | } |
| 5160 | close(fd); |
| 5161 | } |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5162 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5163 | // give the ATTENTION message when there is an old swap file |
| 5164 | // for the current file, and the buffer was not recovered. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5165 | if (differ == FALSE && !(curbuf->b_flags & BF_RECOVERED) |
| 5166 | && vim_strchr(p_shm, SHM_ATTENTION) == NULL) |
| 5167 | { |
Bram Moolenaar | 188639d | 2022-04-04 16:57:21 +0100 | [diff] [blame] | 5168 | sea_choice_T choice = SEA_CHOICE_NONE; |
| 5169 | stat_T st; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5170 | #ifdef CREATE_DUMMY_FILE |
Bram Moolenaar | 188639d | 2022-04-04 16:57:21 +0100 | [diff] [blame] | 5171 | int did_use_dummy = FALSE; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5172 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5173 | // Avoid getting a warning for the file being created |
| 5174 | // outside of Vim, it was created at the start of this |
| 5175 | // function. Delete the file now, because Vim might exit |
| 5176 | // here if the window is closed. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5177 | if (dummyfd != NULL) |
| 5178 | { |
| 5179 | fclose(dummyfd); |
| 5180 | dummyfd = NULL; |
Bram Moolenaar | 69c3500 | 2013-11-04 02:54:12 +0100 | [diff] [blame] | 5181 | mch_remove(buf_fname); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5182 | did_use_dummy = TRUE; |
| 5183 | } |
| 5184 | #endif |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5185 | |
Bram Moolenaar | 1b243ea | 2019-04-28 22:50:40 +0200 | [diff] [blame] | 5186 | #ifdef HAVE_PROCESS_STILL_RUNNING |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5187 | process_still_running = FALSE; |
| 5188 | #endif |
Bram Moolenaar | 67cf86b | 2019-04-28 22:25:38 +0200 | [diff] [blame] | 5189 | // It's safe to delete the swap file if all these are true: |
| 5190 | // - the edited file exists |
| 5191 | // - the swap file has no changes and looks OK |
| 5192 | if (mch_stat((char *)buf->b_fname, &st) == 0 |
| 5193 | && swapfile_unchanged(fname)) |
| 5194 | { |
Bram Moolenaar | 188639d | 2022-04-04 16:57:21 +0100 | [diff] [blame] | 5195 | choice = SEA_CHOICE_DELETE; |
Bram Moolenaar | 67cf86b | 2019-04-28 22:25:38 +0200 | [diff] [blame] | 5196 | if (p_verbose > 0) |
| 5197 | verb_msg(_("Found a swap file that is not useful, deleting it")); |
| 5198 | } |
| 5199 | |
Bram Moolenaar | f2bd8ef | 2018-03-04 18:08:14 +0100 | [diff] [blame] | 5200 | #if defined(FEAT_EVAL) |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 5201 | /* |
| 5202 | * If there is an SwapExists autocommand and we can handle |
| 5203 | * the response, trigger it. It may return 0 to ask the |
| 5204 | * user anyway. |
| 5205 | */ |
Bram Moolenaar | 188639d | 2022-04-04 16:57:21 +0100 | [diff] [blame] | 5206 | if (choice == SEA_CHOICE_NONE |
Bram Moolenaar | 67cf86b | 2019-04-28 22:25:38 +0200 | [diff] [blame] | 5207 | && swap_exists_action != SEA_NONE |
Bram Moolenaar | 69c3500 | 2013-11-04 02:54:12 +0100 | [diff] [blame] | 5208 | && has_autocmd(EVENT_SWAPEXISTS, buf_fname, buf)) |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 5209 | choice = do_swapexists(buf, fname); |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 5210 | #endif |
Bram Moolenaar | 188639d | 2022-04-04 16:57:21 +0100 | [diff] [blame] | 5211 | |
| 5212 | if (choice == SEA_CHOICE_NONE |
| 5213 | && swap_exists_action == SEA_READONLY) |
| 5214 | { |
| 5215 | // always open readonly. |
| 5216 | choice = SEA_CHOICE_READONLY; |
| 5217 | } |
| 5218 | |
| 5219 | if (choice == SEA_CHOICE_NONE) |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 5220 | { |
| 5221 | #ifdef FEAT_GUI |
Bram Moolenaar | 798184c | 2018-10-07 20:48:39 +0200 | [diff] [blame] | 5222 | // If we are supposed to start the GUI but it wasn't |
| 5223 | // completely started yet, start it now. This makes |
| 5224 | // the messages displayed in the Vim window when |
| 5225 | // loading a session from the .gvimrc file. |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 5226 | if (gui.starting && !gui.in_use) |
Bram Moolenaar | afde13b | 2019-04-28 19:46:49 +0200 | [diff] [blame] | 5227 | gui_start(NULL); |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 5228 | #endif |
Bram Moolenaar | 798184c | 2018-10-07 20:48:39 +0200 | [diff] [blame] | 5229 | // Show info about the existing swap file. |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 5230 | attention_message(buf, fname); |
| 5231 | |
Bram Moolenaar | 798184c | 2018-10-07 20:48:39 +0200 | [diff] [blame] | 5232 | // We don't want a 'q' typed at the more-prompt |
| 5233 | // interrupt loading a file. |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 5234 | got_int = FALSE; |
Bram Moolenaar | 798184c | 2018-10-07 20:48:39 +0200 | [diff] [blame] | 5235 | |
| 5236 | // If vimrc has "simalt ~x" we don't want it to |
| 5237 | // interfere with the prompt here. |
Bram Moolenaar | 6a2633b | 2018-10-07 23:16:36 +0200 | [diff] [blame] | 5238 | flush_buffers(FLUSH_TYPEAHEAD); |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 5239 | } |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5240 | |
| 5241 | #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) |
Bram Moolenaar | 188639d | 2022-04-04 16:57:21 +0100 | [diff] [blame] | 5242 | if (swap_exists_action != SEA_NONE |
| 5243 | && choice == SEA_CHOICE_NONE) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5244 | { |
| 5245 | char_u *name; |
Bram Moolenaar | 188639d | 2022-04-04 16:57:21 +0100 | [diff] [blame] | 5246 | int dialog_result; |
John Marriott | 02d7a6c | 2024-02-26 21:21:17 +0100 | [diff] [blame] | 5247 | size_t len = STRLEN(_("Swap file \"")); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5248 | |
Bram Moolenaar | 964b374 | 2019-05-24 18:54:09 +0200 | [diff] [blame] | 5249 | name = alloc(STRLEN(fname) |
John Marriott | 02d7a6c | 2024-02-26 21:21:17 +0100 | [diff] [blame] | 5250 | + len |
Bram Moolenaar | 964b374 | 2019-05-24 18:54:09 +0200 | [diff] [blame] | 5251 | + STRLEN(_("\" already exists!")) + 5); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5252 | if (name != NULL) |
| 5253 | { |
| 5254 | STRCPY(name, _("Swap file \"")); |
John Marriott | 02d7a6c | 2024-02-26 21:21:17 +0100 | [diff] [blame] | 5255 | home_replace(NULL, fname, name + len, 1000, TRUE); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5256 | STRCAT(name, _("\" already exists!")); |
| 5257 | } |
Bram Moolenaar | 188639d | 2022-04-04 16:57:21 +0100 | [diff] [blame] | 5258 | dialog_result = do_dialog(VIM_WARNING, |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5259 | (char_u *)_("VIM - ATTENTION"), |
| 5260 | name == NULL |
| 5261 | ? (char_u *)_("Swap file already exists!") |
| 5262 | : name, |
Bram Moolenaar | 1b243ea | 2019-04-28 22:50:40 +0200 | [diff] [blame] | 5263 | # ifdef HAVE_PROCESS_STILL_RUNNING |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5264 | process_still_running |
| 5265 | ? (char_u *)_("&Open Read-Only\n&Edit anyway\n&Recover\n&Quit\n&Abort") : |
| 5266 | # endif |
Bram Moolenaar | d2c340a | 2011-01-17 20:08:11 +0100 | [diff] [blame] | 5267 | (char_u *)_("&Open Read-Only\n&Edit anyway\n&Recover\n&Delete it\n&Quit\n&Abort"), 1, NULL, FALSE); |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 5268 | |
Bram Moolenaar | 1b243ea | 2019-04-28 22:50:40 +0200 | [diff] [blame] | 5269 | # ifdef HAVE_PROCESS_STILL_RUNNING |
Bram Moolenaar | 188639d | 2022-04-04 16:57:21 +0100 | [diff] [blame] | 5270 | if (process_still_running && dialog_result >= 4) |
| 5271 | // compensate for missing "Delete it" button |
| 5272 | dialog_result++; |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 5273 | # endif |
Bram Moolenaar | 188639d | 2022-04-04 16:57:21 +0100 | [diff] [blame] | 5274 | choice = dialog_result; |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 5275 | vim_free(name); |
| 5276 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5277 | // pretend screen didn't scroll, need redraw anyway |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 5278 | msg_scrolled = 0; |
Bram Moolenaar | a4d158b | 2022-08-14 14:17:45 +0100 | [diff] [blame] | 5279 | redraw_all_later(UPD_NOT_VALID); |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 5280 | } |
| 5281 | #endif |
| 5282 | |
Bram Moolenaar | 188639d | 2022-04-04 16:57:21 +0100 | [diff] [blame] | 5283 | switch (choice) |
Bram Moolenaar | d5bc83f | 2005-12-07 21:07:59 +0000 | [diff] [blame] | 5284 | { |
Bram Moolenaar | 188639d | 2022-04-04 16:57:21 +0100 | [diff] [blame] | 5285 | case SEA_CHOICE_READONLY: |
| 5286 | buf->b_p_ro = TRUE; |
| 5287 | break; |
| 5288 | case SEA_CHOICE_EDIT: |
| 5289 | break; |
| 5290 | case SEA_CHOICE_RECOVER: |
| 5291 | swap_exists_action = SEA_RECOVER; |
| 5292 | break; |
| 5293 | case SEA_CHOICE_DELETE: |
| 5294 | mch_remove(fname); |
| 5295 | break; |
| 5296 | case SEA_CHOICE_QUIT: |
| 5297 | swap_exists_action = SEA_QUIT; |
| 5298 | break; |
| 5299 | case SEA_CHOICE_ABORT: |
| 5300 | swap_exists_action = SEA_QUIT; |
| 5301 | got_int = TRUE; |
| 5302 | break; |
| 5303 | case SEA_CHOICE_NONE: |
| 5304 | msg_puts("\n"); |
| 5305 | if (msg_silent == 0) |
| 5306 | // call wait_return() later |
| 5307 | need_wait_return = TRUE; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5308 | break; |
| 5309 | } |
Bram Moolenaar | 188639d | 2022-04-04 16:57:21 +0100 | [diff] [blame] | 5310 | |
| 5311 | // If the file was deleted this fname can be used. |
| 5312 | if (choice != SEA_CHOICE_NONE && mch_getperm(fname) < 0) |
| 5313 | break; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5314 | |
| 5315 | #ifdef CREATE_DUMMY_FILE |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5316 | // Going to try another name, need the dummy file again. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5317 | if (did_use_dummy) |
Bram Moolenaar | 69c3500 | 2013-11-04 02:54:12 +0100 | [diff] [blame] | 5318 | dummyfd = mch_fopen((char *)buf_fname, "w"); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5319 | #endif |
| 5320 | } |
| 5321 | } |
| 5322 | } |
| 5323 | |
| 5324 | /* |
| 5325 | * Change the ".swp" extension to find another file that can be used. |
| 5326 | * First decrement the last char: ".swo", ".swn", etc. |
| 5327 | * If that still isn't enough decrement the last but one char: ".svz" |
Bram Moolenaar | 69a7cb4 | 2004-06-20 12:51:53 +0000 | [diff] [blame] | 5328 | * Can happen when editing many "No Name" buffers. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5329 | */ |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5330 | if (fname[n - 1] == 'a') // ".s?a" |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5331 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5332 | if (fname[n - 2] == 'a') // ".saa": tried enough, give up |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5333 | { |
Bram Moolenaar | eaaac01 | 2022-01-02 17:00:40 +0000 | [diff] [blame] | 5334 | emsg(_(e_too_many_swap_files_found)); |
Bram Moolenaar | d23a823 | 2018-02-10 18:45:26 +0100 | [diff] [blame] | 5335 | VIM_CLEAR(fname); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5336 | break; |
| 5337 | } |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5338 | --fname[n - 2]; // ".svz", ".suz", etc. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5339 | fname[n - 1] = 'z' + 1; |
| 5340 | } |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5341 | --fname[n - 1]; // ".swo", ".swn", etc. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5342 | } |
| 5343 | |
| 5344 | vim_free(dir_name); |
| 5345 | #ifdef CREATE_DUMMY_FILE |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5346 | if (dummyfd != NULL) // file has been created temporarily |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5347 | { |
| 5348 | fclose(dummyfd); |
Bram Moolenaar | 69c3500 | 2013-11-04 02:54:12 +0100 | [diff] [blame] | 5349 | mch_remove(buf_fname); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5350 | } |
| 5351 | #endif |
Bram Moolenaar | 4f97475 | 2019-02-17 17:44:42 +0100 | [diff] [blame] | 5352 | #ifdef MSWIN |
Bram Moolenaar | 69c3500 | 2013-11-04 02:54:12 +0100 | [diff] [blame] | 5353 | if (buf_fname != buf->b_fname) |
| 5354 | vim_free(buf_fname); |
| 5355 | #endif |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5356 | return fname; |
| 5357 | } |
| 5358 | |
| 5359 | static int |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 5360 | b0_magic_wrong(ZERO_BL *b0p) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5361 | { |
| 5362 | return (b0p->b0_magic_long != (long)B0_MAGIC_LONG |
| 5363 | || b0p->b0_magic_int != (int)B0_MAGIC_INT |
| 5364 | || b0p->b0_magic_short != (short)B0_MAGIC_SHORT |
| 5365 | || b0p->b0_magic_char != B0_MAGIC_CHAR); |
| 5366 | } |
| 5367 | |
| 5368 | #ifdef CHECK_INODE |
| 5369 | /* |
| 5370 | * Compare current file name with file name from swap file. |
| 5371 | * Try to use inode numbers when possible. |
| 5372 | * Return non-zero when files are different. |
| 5373 | * |
| 5374 | * When comparing file names a few things have to be taken into consideration: |
| 5375 | * - When working over a network the full path of a file depends on the host. |
| 5376 | * We check the inode number if possible. It is not 100% reliable though, |
| 5377 | * because the device number cannot be used over a network. |
| 5378 | * - When a file does not exist yet (editing a new file) there is no inode |
| 5379 | * number. |
| 5380 | * - The file name in a swap file may not be valid on the current host. The |
| 5381 | * "~user" form is used whenever possible to avoid this. |
| 5382 | * |
| 5383 | * This is getting complicated, let's make a table: |
| 5384 | * |
| 5385 | * ino_c ino_s fname_c fname_s differ = |
| 5386 | * |
| 5387 | * both files exist -> compare inode numbers: |
| 5388 | * != 0 != 0 X X ino_c != ino_s |
| 5389 | * |
| 5390 | * inode number(s) unknown, file names available -> compare file names |
| 5391 | * == 0 X OK OK fname_c != fname_s |
| 5392 | * X == 0 OK OK fname_c != fname_s |
| 5393 | * |
| 5394 | * current file doesn't exist, file for swap file exist, file name(s) not |
| 5395 | * available -> probably different |
| 5396 | * == 0 != 0 FAIL X TRUE |
| 5397 | * == 0 != 0 X FAIL TRUE |
| 5398 | * |
| 5399 | * current file exists, inode for swap unknown, file name(s) not |
| 5400 | * available -> probably different |
| 5401 | * != 0 == 0 FAIL X TRUE |
| 5402 | * != 0 == 0 X FAIL TRUE |
| 5403 | * |
| 5404 | * current file doesn't exist, inode for swap unknown, one file name not |
| 5405 | * available -> probably different |
| 5406 | * == 0 == 0 FAIL OK TRUE |
| 5407 | * == 0 == 0 OK FAIL TRUE |
| 5408 | * |
| 5409 | * current file doesn't exist, inode for swap unknown, both file names not |
Bram Moolenaar | 8c3169c | 2018-05-12 17:04:12 +0200 | [diff] [blame] | 5410 | * available -> compare file names |
| 5411 | * == 0 == 0 FAIL FAIL fname_c != fname_s |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5412 | * |
| 5413 | * Note that when the ino_t is 64 bits, only the last 32 will be used. This |
| 5414 | * can't be changed without making the block 0 incompatible with 32 bit |
| 5415 | * versions. |
| 5416 | */ |
| 5417 | |
| 5418 | static int |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 5419 | fnamecmp_ino( |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5420 | char_u *fname_c, // current file name |
| 5421 | char_u *fname_s, // file name from swap file |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 5422 | long ino_block0) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5423 | { |
Bram Moolenaar | 8767f52 | 2016-07-01 17:17:39 +0200 | [diff] [blame] | 5424 | stat_T st; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5425 | ino_t ino_c = 0; // ino of current file |
| 5426 | ino_t ino_s; // ino of file from swap file |
| 5427 | char_u buf_c[MAXPATHL]; // full path of fname_c |
| 5428 | char_u buf_s[MAXPATHL]; // full path of fname_s |
| 5429 | int retval_c; // flag: buf_c valid |
| 5430 | int retval_s; // flag: buf_s valid |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5431 | |
| 5432 | if (mch_stat((char *)fname_c, &st) == 0) |
| 5433 | ino_c = (ino_t)st.st_ino; |
| 5434 | |
| 5435 | /* |
| 5436 | * First we try to get the inode from the file name, because the inode in |
| 5437 | * the swap file may be outdated. If that fails (e.g. this path is not |
| 5438 | * valid on this machine), use the inode from block 0. |
| 5439 | */ |
| 5440 | if (mch_stat((char *)fname_s, &st) == 0) |
| 5441 | ino_s = (ino_t)st.st_ino; |
| 5442 | else |
| 5443 | ino_s = (ino_t)ino_block0; |
| 5444 | |
| 5445 | if (ino_c && ino_s) |
| 5446 | return (ino_c != ino_s); |
| 5447 | |
| 5448 | /* |
| 5449 | * One of the inode numbers is unknown, try a forced vim_FullName() and |
| 5450 | * compare the file names. |
| 5451 | */ |
| 5452 | retval_c = vim_FullName(fname_c, buf_c, MAXPATHL, TRUE); |
| 5453 | retval_s = vim_FullName(fname_s, buf_s, MAXPATHL, TRUE); |
| 5454 | if (retval_c == OK && retval_s == OK) |
Bram Moolenaar | 8c3169c | 2018-05-12 17:04:12 +0200 | [diff] [blame] | 5455 | return STRCMP(buf_c, buf_s) != 0; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5456 | |
| 5457 | /* |
| 5458 | * Can't compare inodes or file names, guess that the files are different, |
Bram Moolenaar | 8c3169c | 2018-05-12 17:04:12 +0200 | [diff] [blame] | 5459 | * unless both appear not to exist at all, then compare with the file name |
| 5460 | * in the swap file. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5461 | */ |
| 5462 | if (ino_s == 0 && ino_c == 0 && retval_c == FAIL && retval_s == FAIL) |
Bram Moolenaar | 8c3169c | 2018-05-12 17:04:12 +0200 | [diff] [blame] | 5463 | return STRCMP(fname_c, fname_s) != 0; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5464 | return TRUE; |
| 5465 | } |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5466 | #endif // CHECK_INODE |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5467 | |
| 5468 | /* |
| 5469 | * Move a long integer into a four byte character array. |
| 5470 | * Used for machine independency in block zero. |
| 5471 | */ |
| 5472 | static void |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 5473 | long_to_char(long n, char_u *s) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5474 | { |
| 5475 | s[0] = (char_u)(n & 0xff); |
| 5476 | n = (unsigned)n >> 8; |
| 5477 | s[1] = (char_u)(n & 0xff); |
| 5478 | n = (unsigned)n >> 8; |
| 5479 | s[2] = (char_u)(n & 0xff); |
| 5480 | n = (unsigned)n >> 8; |
| 5481 | s[3] = (char_u)(n & 0xff); |
| 5482 | } |
| 5483 | |
| 5484 | static long |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 5485 | char_to_long(char_u *s) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5486 | { |
| 5487 | long retval; |
| 5488 | |
| 5489 | retval = s[3]; |
| 5490 | retval <<= 8; |
| 5491 | retval |= s[2]; |
| 5492 | retval <<= 8; |
| 5493 | retval |= s[1]; |
| 5494 | retval <<= 8; |
| 5495 | retval |= s[0]; |
| 5496 | |
| 5497 | return retval; |
| 5498 | } |
| 5499 | |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 5500 | /* |
| 5501 | * Set the flags in the first block of the swap file: |
| 5502 | * - file is modified or not: buf->b_changed |
| 5503 | * - 'fileformat' |
| 5504 | * - 'fileencoding' |
| 5505 | */ |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5506 | void |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 5507 | ml_setflags(buf_T *buf) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5508 | { |
| 5509 | bhdr_T *hp; |
| 5510 | ZERO_BL *b0p; |
| 5511 | |
| 5512 | if (!buf->b_ml.ml_mfp) |
| 5513 | return; |
| 5514 | for (hp = buf->b_ml.ml_mfp->mf_used_last; hp != NULL; hp = hp->bh_prev) |
| 5515 | { |
| 5516 | if (hp->bh_bnum == 0) |
| 5517 | { |
| 5518 | b0p = (ZERO_BL *)(hp->bh_data); |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 5519 | b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0; |
| 5520 | b0p->b0_flags = (b0p->b0_flags & ~B0_FF_MASK) |
| 5521 | | (get_fileformat(buf) + 1); |
Bram Moolenaar | 1cd871b | 2004-12-19 22:46:22 +0000 | [diff] [blame] | 5522 | add_b0_fenc(b0p, buf); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5523 | hp->bh_flags |= BH_DIRTY; |
| 5524 | mf_sync(buf->b_ml.ml_mfp, MFS_ZERO); |
| 5525 | break; |
| 5526 | } |
| 5527 | } |
| 5528 | } |
| 5529 | |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5530 | #if defined(FEAT_CRYPT) || defined(PROTO) |
| 5531 | /* |
| 5532 | * If "data" points to a data block encrypt the text in it and return a copy |
| 5533 | * in allocated memory. Return NULL when out of memory. |
| 5534 | * Otherwise return "data". |
| 5535 | */ |
| 5536 | char_u * |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 5537 | ml_encrypt_data( |
| 5538 | memfile_T *mfp, |
| 5539 | char_u *data, |
Bram Moolenaar | 8767f52 | 2016-07-01 17:17:39 +0200 | [diff] [blame] | 5540 | off_T offset, |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 5541 | unsigned size) |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5542 | { |
| 5543 | DATA_BL *dp = (DATA_BL *)data; |
| 5544 | char_u *head_end; |
| 5545 | char_u *text_start; |
| 5546 | char_u *new_data; |
| 5547 | int text_len; |
Bram Moolenaar | 8f4ac01 | 2014-08-10 13:38:34 +0200 | [diff] [blame] | 5548 | cryptstate_T *state; |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5549 | |
| 5550 | if (dp->db_id != DATA_ID) |
| 5551 | return data; |
| 5552 | |
Bram Moolenaar | bc56336 | 2015-06-09 18:35:25 +0200 | [diff] [blame] | 5553 | state = ml_crypt_prepare(mfp, offset, FALSE); |
| 5554 | if (state == NULL) |
| 5555 | return data; |
| 5556 | |
Bram Moolenaar | c799fe2 | 2019-05-28 23:08:19 +0200 | [diff] [blame] | 5557 | new_data = alloc(size); |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5558 | if (new_data == NULL) |
Christian Brabandt | 915f3bf | 2024-04-05 20:12:19 +0200 | [diff] [blame] | 5559 | { |
| 5560 | crypt_free_state(state); |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5561 | return NULL; |
Christian Brabandt | 915f3bf | 2024-04-05 20:12:19 +0200 | [diff] [blame] | 5562 | } |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5563 | head_end = (char_u *)(&dp->db_index[dp->db_line_count]); |
| 5564 | text_start = (char_u *)dp + dp->db_txt_start; |
| 5565 | text_len = size - dp->db_txt_start; |
| 5566 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5567 | // Copy the header and the text. |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5568 | mch_memmove(new_data, dp, head_end - (char_u *)dp); |
| 5569 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5570 | // Encrypt the text. |
Christian Brabandt | f573c6e | 2021-06-20 14:02:16 +0200 | [diff] [blame] | 5571 | crypt_encode(state, text_start, text_len, new_data + dp->db_txt_start, |
| 5572 | FALSE); |
Bram Moolenaar | 8f4ac01 | 2014-08-10 13:38:34 +0200 | [diff] [blame] | 5573 | crypt_free_state(state); |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5574 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5575 | // Clear the gap. |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5576 | if (head_end < text_start) |
| 5577 | vim_memset(new_data + (head_end - data), 0, text_start - head_end); |
| 5578 | |
| 5579 | return new_data; |
| 5580 | } |
| 5581 | |
| 5582 | /* |
Bram Moolenaar | bc56336 | 2015-06-09 18:35:25 +0200 | [diff] [blame] | 5583 | * Decrypt the text in "data" if it points to an encrypted data block. |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5584 | */ |
| 5585 | void |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 5586 | ml_decrypt_data( |
| 5587 | memfile_T *mfp, |
| 5588 | char_u *data, |
Bram Moolenaar | 8767f52 | 2016-07-01 17:17:39 +0200 | [diff] [blame] | 5589 | off_T offset, |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 5590 | unsigned size) |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5591 | { |
| 5592 | DATA_BL *dp = (DATA_BL *)data; |
| 5593 | char_u *head_end; |
| 5594 | char_u *text_start; |
| 5595 | int text_len; |
Bram Moolenaar | 8f4ac01 | 2014-08-10 13:38:34 +0200 | [diff] [blame] | 5596 | cryptstate_T *state; |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5597 | |
Yegappan Lakshmanan | e857598 | 2023-01-14 12:32:28 +0000 | [diff] [blame] | 5598 | if (dp->db_id != DATA_ID) |
| 5599 | return; |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5600 | |
Yegappan Lakshmanan | e857598 | 2023-01-14 12:32:28 +0000 | [diff] [blame] | 5601 | head_end = (char_u *)(&dp->db_index[dp->db_line_count]); |
| 5602 | text_start = (char_u *)dp + dp->db_txt_start; |
| 5603 | text_len = dp->db_txt_end - dp->db_txt_start; |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5604 | |
Yegappan Lakshmanan | e857598 | 2023-01-14 12:32:28 +0000 | [diff] [blame] | 5605 | if (head_end > text_start || dp->db_txt_start > size |
| 5606 | || dp->db_txt_end > size) |
| 5607 | return; // data was messed up |
| 5608 | |
| 5609 | state = ml_crypt_prepare(mfp, offset, TRUE); |
| 5610 | if (state == NULL) |
| 5611 | return; |
| 5612 | |
| 5613 | // Decrypt the text in place. |
| 5614 | crypt_decode_inplace(state, text_start, text_len, FALSE); |
| 5615 | crypt_free_state(state); |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5616 | } |
| 5617 | |
| 5618 | /* |
| 5619 | * Prepare for encryption/decryption, using the key, seed and offset. |
Bram Moolenaar | 8f4ac01 | 2014-08-10 13:38:34 +0200 | [diff] [blame] | 5620 | * Return an allocated cryptstate_T *. |
Christian Brabandt | aae5834 | 2023-04-23 17:50:22 +0100 | [diff] [blame] | 5621 | * Note: Encryption not supported for SODIUM |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5622 | */ |
Bram Moolenaar | 8f4ac01 | 2014-08-10 13:38:34 +0200 | [diff] [blame] | 5623 | static cryptstate_T * |
Bram Moolenaar | 8767f52 | 2016-07-01 17:17:39 +0200 | [diff] [blame] | 5624 | ml_crypt_prepare(memfile_T *mfp, off_T offset, int reading) |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5625 | { |
| 5626 | buf_T *buf = mfp->mf_buffer; |
| 5627 | char_u salt[50]; |
Bram Moolenaar | 8f4ac01 | 2014-08-10 13:38:34 +0200 | [diff] [blame] | 5628 | int method_nr; |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5629 | char_u *key; |
Christian Brabandt | aae5834 | 2023-04-23 17:50:22 +0100 | [diff] [blame] | 5630 | crypt_arg_T arg; |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5631 | |
Christian Brabandt | aae5834 | 2023-04-23 17:50:22 +0100 | [diff] [blame] | 5632 | CLEAR_FIELD(arg); |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5633 | if (reading && mfp->mf_old_key != NULL) |
| 5634 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5635 | // Reading back blocks with the previous key/method/seed. |
Bram Moolenaar | 8f4ac01 | 2014-08-10 13:38:34 +0200 | [diff] [blame] | 5636 | method_nr = mfp->mf_old_cm; |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5637 | key = mfp->mf_old_key; |
Christian Brabandt | aae5834 | 2023-04-23 17:50:22 +0100 | [diff] [blame] | 5638 | arg.cat_seed = mfp->mf_old_seed; |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5639 | } |
| 5640 | else |
| 5641 | { |
Bram Moolenaar | 8f4ac01 | 2014-08-10 13:38:34 +0200 | [diff] [blame] | 5642 | method_nr = crypt_get_method_nr(buf); |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5643 | key = buf->b_p_key; |
Christian Brabandt | aae5834 | 2023-04-23 17:50:22 +0100 | [diff] [blame] | 5644 | arg.cat_seed = mfp->mf_seed; |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5645 | } |
Christian Brabandt | aae5834 | 2023-04-23 17:50:22 +0100 | [diff] [blame] | 5646 | |
Bram Moolenaar | bc56336 | 2015-06-09 18:35:25 +0200 | [diff] [blame] | 5647 | if (*key == NUL) |
| 5648 | return NULL; |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5649 | |
Christian Brabandt | 19e6c4f | 2023-06-27 18:57:10 +0100 | [diff] [blame] | 5650 | if (crypt_may_close_swapfile(buf, key, method_nr)) |
| 5651 | return NULL; |
| 5652 | |
Bram Moolenaar | 8f4ac01 | 2014-08-10 13:38:34 +0200 | [diff] [blame] | 5653 | if (method_nr == CRYPT_M_ZIP) |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5654 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5655 | // For PKzip: Append the offset to the key, so that we use a different |
| 5656 | // key for every block. |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5657 | vim_snprintf((char *)salt, sizeof(salt), "%s%ld", key, (long)offset); |
Christian Brabandt | aae5834 | 2023-04-23 17:50:22 +0100 | [diff] [blame] | 5658 | arg.cat_seed = NULL; |
| 5659 | arg.cat_init_from_file = FALSE; |
| 5660 | |
| 5661 | return crypt_create(method_nr, salt, &arg); |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5662 | } |
Bram Moolenaar | 8f4ac01 | 2014-08-10 13:38:34 +0200 | [diff] [blame] | 5663 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5664 | // Using blowfish or better: add salt and seed. We use the byte offset |
| 5665 | // of the block for the salt. |
Bram Moolenaar | 8f4ac01 | 2014-08-10 13:38:34 +0200 | [diff] [blame] | 5666 | vim_snprintf((char *)salt, sizeof(salt), "%ld", (long)offset); |
Christian Brabandt | aae5834 | 2023-04-23 17:50:22 +0100 | [diff] [blame] | 5667 | |
| 5668 | arg.cat_salt = salt; |
| 5669 | arg.cat_salt_len = (int)STRLEN(salt); |
| 5670 | arg.cat_seed_len = MF_SEED_LEN; |
| 5671 | arg.cat_add_len = 0; |
| 5672 | arg.cat_add = NULL; |
| 5673 | arg.cat_init_from_file = FALSE; |
| 5674 | |
| 5675 | return crypt_create(method_nr, key, &arg); |
Bram Moolenaar | a8ffcbb | 2010-06-21 06:15:46 +0200 | [diff] [blame] | 5676 | } |
| 5677 | |
| 5678 | #endif |
| 5679 | |
| 5680 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5681 | #if defined(FEAT_BYTEOFF) || defined(PROTO) |
| 5682 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5683 | #define MLCS_MAXL 800 // max no of lines in chunk |
| 5684 | #define MLCS_MINL 400 // should be half of MLCS_MAXL |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5685 | |
| 5686 | /* |
Bram Moolenaar | 0ad014c | 2010-07-25 14:00:46 +0200 | [diff] [blame] | 5687 | * Keep information for finding byte offset of a line, updtype may be one of: |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5688 | * ML_CHNK_ADDLINE: Add len to parent chunk, possibly splitting it |
| 5689 | * Careful: ML_CHNK_ADDLINE may cause ml_find_line() to be called. |
| 5690 | * ML_CHNK_DELLINE: Subtract len from parent chunk, possibly deleting it |
| 5691 | * ML_CHNK_UPDLINE: Add len to parent chunk, as a signed entity. |
| 5692 | */ |
| 5693 | static void |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 5694 | ml_updatechunk( |
| 5695 | buf_T *buf, |
| 5696 | linenr_T line, |
| 5697 | long len, |
| 5698 | int updtype) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5699 | { |
| 5700 | static buf_T *ml_upd_lastbuf = NULL; |
| 5701 | static linenr_T ml_upd_lastline; |
| 5702 | static linenr_T ml_upd_lastcurline; |
| 5703 | static int ml_upd_lastcurix; |
| 5704 | |
| 5705 | linenr_T curline = ml_upd_lastcurline; |
| 5706 | int curix = ml_upd_lastcurix; |
| 5707 | long size; |
| 5708 | chunksize_T *curchnk; |
| 5709 | int rest; |
| 5710 | bhdr_T *hp; |
| 5711 | DATA_BL *dp; |
| 5712 | |
| 5713 | if (buf->b_ml.ml_usedchunks == -1 || len == 0) |
| 5714 | return; |
| 5715 | if (buf->b_ml.ml_chunksize == NULL) |
| 5716 | { |
Bram Moolenaar | c799fe2 | 2019-05-28 23:08:19 +0200 | [diff] [blame] | 5717 | buf->b_ml.ml_chunksize = ALLOC_MULT(chunksize_T, 100); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5718 | if (buf->b_ml.ml_chunksize == NULL) |
| 5719 | { |
| 5720 | buf->b_ml.ml_usedchunks = -1; |
| 5721 | return; |
| 5722 | } |
| 5723 | buf->b_ml.ml_numchunks = 100; |
| 5724 | buf->b_ml.ml_usedchunks = 1; |
| 5725 | buf->b_ml.ml_chunksize[0].mlcs_numlines = 1; |
| 5726 | buf->b_ml.ml_chunksize[0].mlcs_totalsize = 1; |
| 5727 | } |
| 5728 | |
| 5729 | if (updtype == ML_CHNK_UPDLINE && buf->b_ml.ml_line_count == 1) |
| 5730 | { |
| 5731 | /* |
| 5732 | * First line in empty buffer from ml_flush_line() -- reset |
| 5733 | */ |
| 5734 | buf->b_ml.ml_usedchunks = 1; |
| 5735 | buf->b_ml.ml_chunksize[0].mlcs_numlines = 1; |
Bram Moolenaar | 98aefe7 | 2018-12-13 22:20:09 +0100 | [diff] [blame] | 5736 | buf->b_ml.ml_chunksize[0].mlcs_totalsize = (long)buf->b_ml.ml_line_len; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5737 | return; |
| 5738 | } |
| 5739 | |
| 5740 | /* |
| 5741 | * Find chunk that our line belongs to, curline will be at start of the |
| 5742 | * chunk. |
| 5743 | */ |
| 5744 | if (buf != ml_upd_lastbuf || line != ml_upd_lastline + 1 |
| 5745 | || updtype != ML_CHNK_ADDLINE) |
| 5746 | { |
| 5747 | for (curline = 1, curix = 0; |
| 5748 | curix < buf->b_ml.ml_usedchunks - 1 |
| 5749 | && line >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines; |
| 5750 | curix++) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5751 | curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5752 | } |
Bram Moolenaar | a9a8e04 | 2018-10-30 22:15:55 +0100 | [diff] [blame] | 5753 | else if (curix < buf->b_ml.ml_usedchunks - 1 |
| 5754 | && line >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5755 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5756 | // Adjust cached curix & curline |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5757 | curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines; |
| 5758 | curix++; |
| 5759 | } |
| 5760 | curchnk = buf->b_ml.ml_chunksize + curix; |
| 5761 | |
| 5762 | if (updtype == ML_CHNK_DELLINE) |
Bram Moolenaar | 5a6404c | 2006-11-01 17:12:57 +0000 | [diff] [blame] | 5763 | len = -len; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5764 | curchnk->mlcs_totalsize += len; |
| 5765 | if (updtype == ML_CHNK_ADDLINE) |
| 5766 | { |
| 5767 | curchnk->mlcs_numlines++; |
| 5768 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5769 | // May resize here so we don't have to do it in both cases below |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5770 | if (buf->b_ml.ml_usedchunks + 1 >= buf->b_ml.ml_numchunks) |
| 5771 | { |
Bram Moolenaar | 9abd5c6 | 2015-02-10 18:34:01 +0100 | [diff] [blame] | 5772 | chunksize_T *t_chunksize = buf->b_ml.ml_chunksize; |
| 5773 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5774 | buf->b_ml.ml_numchunks = buf->b_ml.ml_numchunks * 3 / 2; |
Bram Moolenaar | 51b6eb4 | 2020-08-22 15:19:18 +0200 | [diff] [blame] | 5775 | buf->b_ml.ml_chunksize = vim_realloc(buf->b_ml.ml_chunksize, |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5776 | sizeof(chunksize_T) * buf->b_ml.ml_numchunks); |
| 5777 | if (buf->b_ml.ml_chunksize == NULL) |
| 5778 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5779 | // Hmmmm, Give up on offset for this buffer |
Bram Moolenaar | 9abd5c6 | 2015-02-10 18:34:01 +0100 | [diff] [blame] | 5780 | vim_free(t_chunksize); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5781 | buf->b_ml.ml_usedchunks = -1; |
| 5782 | return; |
| 5783 | } |
| 5784 | } |
| 5785 | |
| 5786 | if (buf->b_ml.ml_chunksize[curix].mlcs_numlines >= MLCS_MAXL) |
| 5787 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5788 | int count; // number of entries in block |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5789 | int idx; |
Bram Moolenaar | b413d2e | 2018-12-25 23:15:46 +0100 | [diff] [blame] | 5790 | int end_idx; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5791 | int text_end; |
| 5792 | int linecnt; |
| 5793 | |
| 5794 | mch_memmove(buf->b_ml.ml_chunksize + curix + 1, |
| 5795 | buf->b_ml.ml_chunksize + curix, |
| 5796 | (buf->b_ml.ml_usedchunks - curix) * |
| 5797 | sizeof(chunksize_T)); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5798 | // Compute length of first half of lines in the split chunk |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5799 | size = 0; |
| 5800 | linecnt = 0; |
| 5801 | while (curline < buf->b_ml.ml_line_count |
| 5802 | && linecnt < MLCS_MINL) |
| 5803 | { |
| 5804 | if ((hp = ml_find_line(buf, curline, ML_FIND)) == NULL) |
| 5805 | { |
| 5806 | buf->b_ml.ml_usedchunks = -1; |
| 5807 | return; |
| 5808 | } |
| 5809 | dp = (DATA_BL *)(hp->bh_data); |
| 5810 | count = (long)(buf->b_ml.ml_locked_high) - |
| 5811 | (long)(buf->b_ml.ml_locked_low) + 1; |
| 5812 | idx = curline - buf->b_ml.ml_locked_low; |
| 5813 | curline = buf->b_ml.ml_locked_high + 1; |
Bram Moolenaar | b413d2e | 2018-12-25 23:15:46 +0100 | [diff] [blame] | 5814 | |
| 5815 | // compute index of last line to use in this MEMLINE |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5816 | rest = count - idx; |
| 5817 | if (linecnt + rest > MLCS_MINL) |
| 5818 | { |
Bram Moolenaar | b413d2e | 2018-12-25 23:15:46 +0100 | [diff] [blame] | 5819 | end_idx = idx + MLCS_MINL - linecnt - 1; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5820 | linecnt = MLCS_MINL; |
| 5821 | } |
| 5822 | else |
| 5823 | { |
Bram Moolenaar | b413d2e | 2018-12-25 23:15:46 +0100 | [diff] [blame] | 5824 | end_idx = count - 1; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5825 | linecnt += rest; |
| 5826 | } |
Bram Moolenaar | 05ad5ff | 2019-11-30 22:48:27 +0100 | [diff] [blame] | 5827 | #ifdef FEAT_PROP_POPUP |
Bram Moolenaar | b413d2e | 2018-12-25 23:15:46 +0100 | [diff] [blame] | 5828 | if (buf->b_has_textprop) |
| 5829 | { |
| 5830 | int i; |
| 5831 | |
| 5832 | // We cannot use the text pointers to get the text length, |
| 5833 | // the text prop info would also be counted. Go over the |
| 5834 | // lines. |
| 5835 | for (i = end_idx; i < idx; ++i) |
Bram Moolenaar | e5a0e8c | 2022-08-09 21:37:55 +0100 | [diff] [blame] | 5836 | size += (int)STRLEN((char_u *)dp |
| 5837 | + (dp->db_index[i] & DB_INDEX_MASK)) + 1; |
Bram Moolenaar | b413d2e | 2018-12-25 23:15:46 +0100 | [diff] [blame] | 5838 | } |
| 5839 | else |
| 5840 | #endif |
| 5841 | { |
Bram Moolenaar | 14c7530 | 2021-08-15 14:28:40 +0200 | [diff] [blame] | 5842 | if (idx == 0) // first line in block, text at the end |
Bram Moolenaar | b413d2e | 2018-12-25 23:15:46 +0100 | [diff] [blame] | 5843 | text_end = dp->db_txt_end; |
| 5844 | else |
| 5845 | text_end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK); |
Bram Moolenaar | e5a0e8c | 2022-08-09 21:37:55 +0100 | [diff] [blame] | 5846 | size += text_end |
| 5847 | - ((dp->db_index[end_idx]) & DB_INDEX_MASK); |
Bram Moolenaar | b413d2e | 2018-12-25 23:15:46 +0100 | [diff] [blame] | 5848 | } |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5849 | } |
| 5850 | buf->b_ml.ml_chunksize[curix].mlcs_numlines = linecnt; |
| 5851 | buf->b_ml.ml_chunksize[curix + 1].mlcs_numlines -= linecnt; |
| 5852 | buf->b_ml.ml_chunksize[curix].mlcs_totalsize = size; |
| 5853 | buf->b_ml.ml_chunksize[curix + 1].mlcs_totalsize -= size; |
| 5854 | buf->b_ml.ml_usedchunks++; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5855 | ml_upd_lastbuf = NULL; // Force recalc of curix & curline |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5856 | return; |
| 5857 | } |
| 5858 | else if (buf->b_ml.ml_chunksize[curix].mlcs_numlines >= MLCS_MINL |
| 5859 | && curix == buf->b_ml.ml_usedchunks - 1 |
| 5860 | && buf->b_ml.ml_line_count - line <= 1) |
| 5861 | { |
| 5862 | /* |
Bram Moolenaar | 8e7d622 | 2020-12-18 19:49:56 +0100 | [diff] [blame] | 5863 | * We are in the last chunk and it is cheap to create a new one |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5864 | * after this. Do it now to avoid the loop above later on |
| 5865 | */ |
| 5866 | curchnk = buf->b_ml.ml_chunksize + curix + 1; |
| 5867 | buf->b_ml.ml_usedchunks++; |
| 5868 | if (line == buf->b_ml.ml_line_count) |
| 5869 | { |
| 5870 | curchnk->mlcs_numlines = 0; |
| 5871 | curchnk->mlcs_totalsize = 0; |
| 5872 | } |
| 5873 | else |
| 5874 | { |
| 5875 | /* |
| 5876 | * Line is just prior to last, move count for last |
| 5877 | * This is the common case when loading a new file |
| 5878 | */ |
| 5879 | hp = ml_find_line(buf, buf->b_ml.ml_line_count, ML_FIND); |
| 5880 | if (hp == NULL) |
| 5881 | { |
| 5882 | buf->b_ml.ml_usedchunks = -1; |
| 5883 | return; |
| 5884 | } |
| 5885 | dp = (DATA_BL *)(hp->bh_data); |
| 5886 | if (dp->db_line_count == 1) |
| 5887 | rest = dp->db_txt_end - dp->db_txt_start; |
| 5888 | else |
| 5889 | rest = |
| 5890 | ((dp->db_index[dp->db_line_count - 2]) & DB_INDEX_MASK) |
| 5891 | - dp->db_txt_start; |
| 5892 | curchnk->mlcs_totalsize = rest; |
| 5893 | curchnk->mlcs_numlines = 1; |
| 5894 | curchnk[-1].mlcs_totalsize -= rest; |
| 5895 | curchnk[-1].mlcs_numlines -= 1; |
| 5896 | } |
| 5897 | } |
| 5898 | } |
| 5899 | else if (updtype == ML_CHNK_DELLINE) |
| 5900 | { |
| 5901 | curchnk->mlcs_numlines--; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5902 | ml_upd_lastbuf = NULL; // Force recalc of curix & curline |
Bram Moolenaar | e5a0e8c | 2022-08-09 21:37:55 +0100 | [diff] [blame] | 5903 | if (curix < buf->b_ml.ml_usedchunks - 1 |
| 5904 | && curchnk->mlcs_numlines + curchnk[1].mlcs_numlines |
| 5905 | <= MLCS_MINL) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5906 | { |
| 5907 | curix++; |
| 5908 | curchnk = buf->b_ml.ml_chunksize + curix; |
| 5909 | } |
| 5910 | else if (curix == 0 && curchnk->mlcs_numlines <= 0) |
| 5911 | { |
| 5912 | buf->b_ml.ml_usedchunks--; |
| 5913 | mch_memmove(buf->b_ml.ml_chunksize, buf->b_ml.ml_chunksize + 1, |
| 5914 | buf->b_ml.ml_usedchunks * sizeof(chunksize_T)); |
| 5915 | return; |
| 5916 | } |
| 5917 | else if (curix == 0 || (curchnk->mlcs_numlines > 10 |
Bram Moolenaar | e5a0e8c | 2022-08-09 21:37:55 +0100 | [diff] [blame] | 5918 | && curchnk->mlcs_numlines + curchnk[-1].mlcs_numlines |
| 5919 | > MLCS_MINL)) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5920 | { |
| 5921 | return; |
| 5922 | } |
| 5923 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5924 | // Collapse chunks |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5925 | curchnk[-1].mlcs_numlines += curchnk->mlcs_numlines; |
| 5926 | curchnk[-1].mlcs_totalsize += curchnk->mlcs_totalsize; |
| 5927 | buf->b_ml.ml_usedchunks--; |
| 5928 | if (curix < buf->b_ml.ml_usedchunks) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5929 | mch_memmove(buf->b_ml.ml_chunksize + curix, |
| 5930 | buf->b_ml.ml_chunksize + curix + 1, |
| 5931 | (buf->b_ml.ml_usedchunks - curix) * |
| 5932 | sizeof(chunksize_T)); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5933 | return; |
| 5934 | } |
| 5935 | ml_upd_lastbuf = buf; |
| 5936 | ml_upd_lastline = line; |
| 5937 | ml_upd_lastcurline = curline; |
| 5938 | ml_upd_lastcurix = curix; |
| 5939 | } |
| 5940 | |
| 5941 | /* |
| 5942 | * Find offset for line or line with offset. |
Bram Moolenaar | 5313dcb | 2005-02-22 08:56:13 +0000 | [diff] [blame] | 5943 | * Find line with offset if "lnum" is 0; return remaining offset in offp |
| 5944 | * Find offset of line if "lnum" > 0 |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5945 | * return -1 if information is not available |
| 5946 | */ |
| 5947 | long |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 5948 | ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5949 | { |
| 5950 | linenr_T curline; |
| 5951 | int curix; |
| 5952 | long size; |
| 5953 | bhdr_T *hp; |
| 5954 | DATA_BL *dp; |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5955 | int count; // number of entries in block |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5956 | int idx; |
| 5957 | int start_idx; |
| 5958 | int text_end; |
| 5959 | long offset; |
| 5960 | int len; |
| 5961 | int ffdos = (get_fileformat(buf) == EOL_DOS); |
| 5962 | int extra = 0; |
| 5963 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5964 | // take care of cached line first |
Bram Moolenaar | 5313dcb | 2005-02-22 08:56:13 +0000 | [diff] [blame] | 5965 | ml_flush_line(curbuf); |
| 5966 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5967 | if (buf->b_ml.ml_usedchunks == -1 |
| 5968 | || buf->b_ml.ml_chunksize == NULL |
Bram Moolenaar | 5313dcb | 2005-02-22 08:56:13 +0000 | [diff] [blame] | 5969 | || lnum < 0) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5970 | return -1; |
| 5971 | |
| 5972 | if (offp == NULL) |
| 5973 | offset = 0; |
| 5974 | else |
| 5975 | offset = *offp; |
Bram Moolenaar | 5313dcb | 2005-02-22 08:56:13 +0000 | [diff] [blame] | 5976 | if (lnum == 0 && offset <= 0) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 5977 | return 1; // Not a "find offset" and offset 0 _must_ be in line 1 |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5978 | /* |
| 5979 | * Find the last chunk before the one containing our line. Last chunk is |
Bram Moolenaar | 14c7530 | 2021-08-15 14:28:40 +0200 | [diff] [blame] | 5980 | * special because it will never qualify. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5981 | */ |
| 5982 | curline = 1; |
| 5983 | curix = size = 0; |
| 5984 | while (curix < buf->b_ml.ml_usedchunks - 1 |
Bram Moolenaar | 5313dcb | 2005-02-22 08:56:13 +0000 | [diff] [blame] | 5985 | && ((lnum != 0 |
| 5986 | && lnum >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5987 | || (offset != 0 |
| 5988 | && offset > size + buf->b_ml.ml_chunksize[curix].mlcs_totalsize |
=?UTF-8?q?Dundar=20G=C3=B6c?= | d5cec1f | 2022-01-29 15:19:23 +0000 | [diff] [blame] | 5989 | + (long)ffdos * buf->b_ml.ml_chunksize[curix].mlcs_numlines))) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5990 | { |
| 5991 | curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines; |
| 5992 | size += buf->b_ml.ml_chunksize[curix].mlcs_totalsize; |
| 5993 | if (offset && ffdos) |
| 5994 | size += buf->b_ml.ml_chunksize[curix].mlcs_numlines; |
| 5995 | curix++; |
| 5996 | } |
| 5997 | |
Bram Moolenaar | 5313dcb | 2005-02-22 08:56:13 +0000 | [diff] [blame] | 5998 | while ((lnum != 0 && curline < lnum) || (offset != 0 && size < offset)) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 5999 | { |
Bram Moolenaar | 59ff640 | 2021-01-30 17:16:28 +0100 | [diff] [blame] | 6000 | #ifdef FEAT_PROP_POPUP |
| 6001 | size_t textprop_total = 0; |
| 6002 | #endif |
| 6003 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 6004 | if (curline > buf->b_ml.ml_line_count |
| 6005 | || (hp = ml_find_line(buf, curline, ML_FIND)) == NULL) |
| 6006 | return -1; |
| 6007 | dp = (DATA_BL *)(hp->bh_data); |
| 6008 | count = (long)(buf->b_ml.ml_locked_high) - |
| 6009 | (long)(buf->b_ml.ml_locked_low) + 1; |
| 6010 | start_idx = idx = curline - buf->b_ml.ml_locked_low; |
Bram Moolenaar | 9df53b6 | 2020-01-13 20:40:51 +0100 | [diff] [blame] | 6011 | if (idx == 0) // first line in block, text at the end |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 6012 | text_end = dp->db_txt_end; |
| 6013 | else |
| 6014 | text_end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 6015 | // Compute index of last line to use in this MEMLINE |
Bram Moolenaar | 5313dcb | 2005-02-22 08:56:13 +0000 | [diff] [blame] | 6016 | if (lnum != 0) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 6017 | { |
Bram Moolenaar | 5313dcb | 2005-02-22 08:56:13 +0000 | [diff] [blame] | 6018 | if (curline + (count - idx) >= lnum) |
| 6019 | idx += lnum - curline - 1; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 6020 | else |
| 6021 | idx = count - 1; |
| 6022 | } |
| 6023 | else |
| 6024 | { |
| 6025 | extra = 0; |
Bram Moolenaar | 9df53b6 | 2020-01-13 20:40:51 +0100 | [diff] [blame] | 6026 | for (;;) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 6027 | { |
Bram Moolenaar | 9df53b6 | 2020-01-13 20:40:51 +0100 | [diff] [blame] | 6028 | #ifdef FEAT_PROP_POPUP |
Bram Moolenaar | 59ff640 | 2021-01-30 17:16:28 +0100 | [diff] [blame] | 6029 | size_t textprop_size = 0; |
| 6030 | |
Bram Moolenaar | 9df53b6 | 2020-01-13 20:40:51 +0100 | [diff] [blame] | 6031 | if (buf->b_has_textprop) |
| 6032 | { |
Bram Moolenaar | 59ff640 | 2021-01-30 17:16:28 +0100 | [diff] [blame] | 6033 | char_u *l1, *l2; |
| 6034 | |
Bram Moolenaar | 9df53b6 | 2020-01-13 20:40:51 +0100 | [diff] [blame] | 6035 | // compensate for the extra bytes taken by textprops |
| 6036 | l1 = (char_u *)dp + ((dp->db_index[idx]) & DB_INDEX_MASK); |
| 6037 | l2 = (char_u *)dp + (idx == 0 ? dp->db_txt_end |
| 6038 | : ((dp->db_index[idx - 1]) & DB_INDEX_MASK)); |
| 6039 | textprop_size = (l2 - l1) - (STRLEN(l1) + 1); |
| 6040 | } |
| 6041 | #endif |
| 6042 | if (!(offset >= size |
| 6043 | + text_end - (int)((dp->db_index[idx]) & DB_INDEX_MASK) |
| 6044 | #ifdef FEAT_PROP_POPUP |
Bram Moolenaar | 94b6fb7 | 2020-01-17 21:00:59 +0100 | [diff] [blame] | 6045 | - (long)(textprop_total + textprop_size) |
Bram Moolenaar | 9df53b6 | 2020-01-13 20:40:51 +0100 | [diff] [blame] | 6046 | #endif |
| 6047 | + ffdos)) |
| 6048 | break; |
| 6049 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 6050 | if (ffdos) |
| 6051 | size++; |
Bram Moolenaar | 9df53b6 | 2020-01-13 20:40:51 +0100 | [diff] [blame] | 6052 | #ifdef FEAT_PROP_POPUP |
| 6053 | textprop_total += textprop_size; |
| 6054 | #endif |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 6055 | if (idx == count - 1) |
| 6056 | { |
| 6057 | extra = 1; |
| 6058 | break; |
| 6059 | } |
| 6060 | idx++; |
| 6061 | } |
| 6062 | } |
Bram Moolenaar | 05ad5ff | 2019-11-30 22:48:27 +0100 | [diff] [blame] | 6063 | #ifdef FEAT_PROP_POPUP |
Bram Moolenaar | 59ff640 | 2021-01-30 17:16:28 +0100 | [diff] [blame] | 6064 | if (buf->b_has_textprop && lnum != 0) |
Bram Moolenaar | b413d2e | 2018-12-25 23:15:46 +0100 | [diff] [blame] | 6065 | { |
| 6066 | int i; |
| 6067 | |
| 6068 | // cannot use the db_index pointer, need to get the actual text |
| 6069 | // lengths. |
| 6070 | len = 0; |
| 6071 | for (i = start_idx; i <= idx; ++i) |
Bram Moolenaar | 59ff640 | 2021-01-30 17:16:28 +0100 | [diff] [blame] | 6072 | { |
| 6073 | char_u *p = (char_u *)dp + ((dp->db_index[i]) & DB_INDEX_MASK); |
| 6074 | len += (int)STRLEN(p) + 1; |
| 6075 | } |
Bram Moolenaar | b413d2e | 2018-12-25 23:15:46 +0100 | [diff] [blame] | 6076 | } |
| 6077 | else |
| 6078 | #endif |
Bram Moolenaar | 59ff640 | 2021-01-30 17:16:28 +0100 | [diff] [blame] | 6079 | len = text_end - ((dp->db_index[idx]) & DB_INDEX_MASK) |
| 6080 | #ifdef FEAT_PROP_POPUP |
| 6081 | - (long)textprop_total |
| 6082 | #endif |
| 6083 | ; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 6084 | size += len; |
| 6085 | if (offset != 0 && size >= offset) |
| 6086 | { |
| 6087 | if (size + ffdos == offset) |
| 6088 | *offp = 0; |
| 6089 | else if (idx == start_idx) |
| 6090 | *offp = offset - size + len; |
| 6091 | else |
| 6092 | *offp = offset - size + len |
Bram Moolenaar | 59ff640 | 2021-01-30 17:16:28 +0100 | [diff] [blame] | 6093 | - (text_end - ((dp->db_index[idx - 1]) & DB_INDEX_MASK)) |
| 6094 | #ifdef FEAT_PROP_POPUP |
| 6095 | + (long)textprop_total |
| 6096 | #endif |
| 6097 | ; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 6098 | curline += idx - start_idx + extra; |
| 6099 | if (curline > buf->b_ml.ml_line_count) |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 6100 | return -1; // exactly one byte beyond the end |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 6101 | return curline; |
| 6102 | } |
| 6103 | curline = buf->b_ml.ml_locked_high + 1; |
| 6104 | } |
| 6105 | |
Bram Moolenaar | 5313dcb | 2005-02-22 08:56:13 +0000 | [diff] [blame] | 6106 | if (lnum != 0) |
Bram Moolenaar | 5eb86f9 | 2004-07-26 12:53:41 +0000 | [diff] [blame] | 6107 | { |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 6108 | // Count extra CR characters. |
Bram Moolenaar | 5eb86f9 | 2004-07-26 12:53:41 +0000 | [diff] [blame] | 6109 | if (ffdos) |
Bram Moolenaar | 5313dcb | 2005-02-22 08:56:13 +0000 | [diff] [blame] | 6110 | size += lnum - 1; |
Bram Moolenaar | 5eb86f9 | 2004-07-26 12:53:41 +0000 | [diff] [blame] | 6111 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 6112 | // Don't count the last line break if 'noeol' and ('bin' or |
| 6113 | // 'nofixeol'). |
Bram Moolenaar | 34d72d4 | 2015-07-17 14:18:08 +0200 | [diff] [blame] | 6114 | if ((!buf->b_p_fixeol || buf->b_p_bin) && !buf->b_p_eol |
Bram Moolenaar | c26f7c6 | 2018-08-20 22:53:04 +0200 | [diff] [blame] | 6115 | && lnum > buf->b_ml.ml_line_count) |
Bram Moolenaar | 5eb86f9 | 2004-07-26 12:53:41 +0000 | [diff] [blame] | 6116 | size -= ffdos + 1; |
| 6117 | } |
| 6118 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 6119 | return size; |
| 6120 | } |
| 6121 | |
| 6122 | /* |
| 6123 | * Goto byte in buffer with offset 'cnt'. |
| 6124 | */ |
| 6125 | void |
Bram Moolenaar | 52ea13d | 2016-01-30 18:51:09 +0100 | [diff] [blame] | 6126 | goto_byte(long cnt) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 6127 | { |
| 6128 | long boff = cnt; |
| 6129 | linenr_T lnum; |
| 6130 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 6131 | ml_flush_line(curbuf); // cached line may be dirty |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 6132 | setpcmark(); |
| 6133 | if (boff) |
| 6134 | --boff; |
| 6135 | lnum = ml_find_line_or_offset(curbuf, (linenr_T)0, &boff); |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 6136 | if (lnum < 1) // past the end |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 6137 | { |
| 6138 | curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; |
| 6139 | curwin->w_curswant = MAXCOL; |
| 6140 | coladvance((colnr_T)MAXCOL); |
| 6141 | } |
| 6142 | else |
| 6143 | { |
| 6144 | curwin->w_cursor.lnum = lnum; |
| 6145 | curwin->w_cursor.col = (colnr_T)boff; |
Bram Moolenaar | 943d2b5 | 2005-12-02 00:50:49 +0000 | [diff] [blame] | 6146 | curwin->w_cursor.coladd = 0; |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 6147 | curwin->w_set_curswant = TRUE; |
| 6148 | } |
| 6149 | check_cursor(); |
| 6150 | |
Bram Moolenaar | 4ba37b5 | 2019-12-04 21:57:43 +0100 | [diff] [blame] | 6151 | // Make sure the cursor is on the first byte of a multi-byte char. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 6152 | if (has_mbyte) |
| 6153 | mb_adjust_cursor(); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 6154 | } |
| 6155 | #endif |