diff options
Diffstat (limited to 'src/nvim/memline.c')
-rw-r--r-- | src/nvim/memline.c | 1087 |
1 files changed, 589 insertions, 498 deletions
diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 10a8195e7a..3c671121b7 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -1,6 +1,3 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - // for debugging // #define CHECK(c, s) do { if (c) emsg(s); } while (0) #define CHECK(c, s) do {} while (0) @@ -39,14 +36,16 @@ #include <fcntl.h> #include <inttypes.h> #include <stdbool.h> +#include <stddef.h> #include <stdio.h> #include <string.h> +#include <sys/types.h> #include <time.h> #include <uv.h> #include "auto/config.h" #include "klib/kvec.h" -#include "nvim/ascii.h" +#include "nvim/ascii_defs.h" #include "nvim/autocmd.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" @@ -55,16 +54,17 @@ #include "nvim/drawscreen.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" -#include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/fileio.h" +#include "nvim/func_attr.h" #include "nvim/getchar.h" #include "nvim/gettext.h" #include "nvim/globals.h" -#include "nvim/highlight_defs.h" +#include "nvim/highlight.h" #include "nvim/input.h" -#include "nvim/macros.h" +#include "nvim/macros_defs.h" #include "nvim/main.h" +#include "nvim/map_defs.h" #include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memfile.h" @@ -72,31 +72,26 @@ #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/option.h" +#include "nvim/option_vars.h" #include "nvim/os/fs.h" #include "nvim/os/input.h" #include "nvim/os/os.h" #include "nvim/os/process.h" #include "nvim/os/time.h" #include "nvim/path.h" -#include "nvim/pos.h" -#include "nvim/screen.h" +#include "nvim/pos_defs.h" #include "nvim/spell.h" +#include "nvim/statusline.h" #include "nvim/strings.h" -#include "nvim/types.h" #include "nvim/ui.h" #include "nvim/undo.h" #include "nvim/version.h" -#include "nvim/vim.h" +#include "nvim/vim_defs.h" #ifndef UNIX // it's in os/unix_defs.h for Unix # include <time.h> #endif -typedef struct block0 ZERO_BL; // contents of the first block -typedef struct pointer_block PTR_BL; // contents of a pointer block -typedef struct data_block DATA_BL; // contents of a data block -typedef struct pointer_entry PTR_EN; // block/line-count pair - enum { DATA_ID = (('d' << 8) + 'a'), // data block id PTR_ID = (('p' << 8) + 't'), // pointer block id @@ -105,39 +100,43 @@ enum { }; // pointer to a block, used in a pointer block -struct pointer_entry { +typedef struct { blocknr_T pe_bnum; // block number linenr_T pe_line_count; // number of lines in this branch linenr_T pe_old_lnum; // lnum for this block (for recovery) int pe_page_count; // number of pages in block pe_bnum -}; +} PointerEntry; // A pointer block contains a list of branches in the tree. -struct pointer_block { +typedef struct { uint16_t pb_id; // ID for pointer block: PTR_ID uint16_t pb_count; // number of pointers in this block uint16_t pb_count_max; // maximum value for pb_count - PTR_EN pb_pointer[1]; // list of pointers to blocks (actually longer) + PointerEntry pb_pointer[]; // list of pointers to blocks // followed by empty space until end of page -}; +} PointerBlock; + +// Value for pb_count_max. +#define PB_COUNT_MAX(mfp) \ + (uint16_t)((mfp->mf_page_size - offsetof(PointerBlock, pb_pointer)) / sizeof(PointerEntry)) // A data block is a leaf in the tree. // // The text of the lines is at the end of the block. The text of the first line // in the block is put at the end, the text of the second line in front of it, // etc. Thus the order of the lines is the opposite of the line number. -struct data_block { +typedef struct { uint16_t db_id; // ID for data block: DATA_ID unsigned db_free; // free space available unsigned db_txt_start; // byte where text starts unsigned db_txt_end; // byte just after data block // linenr_T db_line_count; long db_line_count; // number of lines in this block - unsigned db_index[1]; // index for start of line (actually bigger) + unsigned db_index[]; // index for start of line // followed by empty space up to db_txt_start // followed by the text in the lines until // end of page -}; +} DataBlock; // The low bits of db_index hold the actual index. The topmost bit is // used for the global command to be able to mark a line. @@ -149,7 +148,7 @@ struct data_block { #define DB_INDEX_MASK (~DB_MARKED) #define INDEX_SIZE (sizeof(unsigned)) // size of one db_index entry -#define HEADER_SIZE (sizeof(DATA_BL) - INDEX_SIZE) // size of data block header +#define HEADER_SIZE (offsetof(DataBlock, db_index)) // size of data block header enum { B0_FNAME_SIZE_ORG = 900, // what it was in older versions @@ -162,37 +161,36 @@ enum { // This won't detect a 64 bit machine that only swaps a byte in the top 32 // bits, but that is crazy anyway. enum { - B0_MAGIC_LONG = 0x30313233L, - B0_MAGIC_INT = 0x20212223L, - B0_MAGIC_SHORT = 0x10111213L, + B0_MAGIC_LONG = 0x30313233, + B0_MAGIC_INT = 0x20212223, + B0_MAGIC_SHORT = 0x10111213, B0_MAGIC_CHAR = 0x55, }; -// Block zero holds all info about the swap file. -// -// NOTE: DEFINITION OF BLOCK 0 SHOULD NOT CHANGE! It would make all existing -// swap files unusable! -// -// If size of block0 changes anyway, adjust MIN_SWAP_PAGE_SIZE in vim.h!! -// -// This block is built up of single bytes, to make it portable across -// different machines. b0_magic_* is used to check the byte order and size of -// variables, because the rest of the swap file is not portable. -struct block0 { - char_u b0_id[2]; ///< ID for block 0: BLOCK0_ID0 and BLOCK0_ID1. - char b0_version[10]; // Vim version string - char_u b0_page_size[4]; // number of bytes per page - char_u b0_mtime[4]; // last modification time of file - char_u b0_ino[4]; // inode of b0_fname - char_u b0_pid[4]; // process id of creator (or 0) - char_u b0_uname[B0_UNAME_SIZE]; // name of user (uid if no name) - char_u b0_hname[B0_HNAME_SIZE]; // host name (if it has a name) - char b0_fname[B0_FNAME_SIZE_ORG]; // name of file being edited - long b0_magic_long; // check for byte order of long - int b0_magic_int; // check for byte order of int - int16_t b0_magic_short; // check for byte order of short - char_u b0_magic_char; // check for last char -}; +/// Block zero holds all info about the swapfile. This is the first block in the file. +/// +/// NOTE: DEFINITION OF BLOCK 0 SHOULD NOT CHANGE! It would make all existing swapfiles unusable! +/// +/// If size of block0 changes anyway, adjust MIN_SWAP_PAGE_SIZE in memfile.h!! +/// +/// This block is built up of single bytes, to make it portable across +/// different machines. b0_magic_* is used to check the byte order and size of +/// variables, because the rest of the swapfile is not portable. +typedef struct { + char b0_id[2]; ///< ID for block 0: BLOCK0_ID0 and BLOCK0_ID1. + char b0_version[10]; ///< Vim version string + char b0_page_size[4]; ///< number of bytes per page + char b0_mtime[4]; ///< last modification time of file + char b0_ino[4]; ///< inode of b0_fname + char b0_pid[4]; ///< process id of creator (or 0) + char b0_uname[B0_UNAME_SIZE]; ///< name of user (uid if no name) + char b0_hname[B0_HNAME_SIZE]; ///< host name (if it has a name) + char b0_fname[B0_FNAME_SIZE_ORG]; ///< name of file being edited + long b0_magic_long; ///< check for byte order of long + int b0_magic_int; ///< check for byte order of int + int16_t b0_magic_short; ///< check for byte order of short + char b0_magic_char; ///< check for last char +} ZeroBlock; // Note: b0_dirty and b0_flags are put at the end of the file name. For very // long file names in older versions of Vim they are invalid. @@ -209,8 +207,7 @@ struct block0 { // EOL_MAC + 1. #define B0_FF_MASK 3 -// Swap file is in directory of edited file. Used to find the file from -// different mount points. +// Swapfile is in directory of edited file. Used to find the file from different mount points. #define B0_SAME_DIR 4 // The 'fileencoding' is at the end of b0_fname[], with a NUL in front of it. @@ -240,17 +237,48 @@ typedef enum { UB_SAME_DIR, // update the B0_SAME_DIR flag } upd_block0_T; +typedef enum { + SEA_CHOICE_NONE = 0, + SEA_CHOICE_READONLY = 1, + SEA_CHOICE_EDIT = 2, + SEA_CHOICE_RECOVER = 3, + SEA_CHOICE_DELETE = 4, + SEA_CHOICE_QUIT = 5, + SEA_CHOICE_ABORT = 6, +} sea_choice_T; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "memline.c.generated.h" #endif +static const char e_ml_get_invalid_lnum_nr[] + = N_("E315: ml_get: Invalid lnum: %" PRId64); +static const char e_ml_get_cannot_find_line_nr_in_buffer_nr_str[] + = N_("E316: ml_get: Cannot find line %" PRId64 "in buffer %d %s"); +static const char e_pointer_block_id_wrong[] + = N_("E317: Pointer block id wrong"); +static const char e_pointer_block_id_wrong_two[] + = N_("E317: Pointer block id wrong 2"); +static const char e_pointer_block_id_wrong_three[] + = N_("E317: Pointer block id wrong 3"); +static const char e_pointer_block_id_wrong_four[] + = N_("E317: Pointer block id wrong 4"); +static const char e_line_number_out_of_range_nr_past_the_end[] + = N_("E322: Line number out of range: %" PRId64 " past the end"); +static const char e_line_count_wrong_in_block_nr[] + = N_("E323: Line count wrong in block %" PRId64); +static const char e_warning_pointer_block_corrupted[] + = N_("E1364: Warning: Pointer block corrupted"); + +#if __has_feature(address_sanitizer) +# define ML_GET_ALLOC_LINES +#endif + /// Open a new memline for "buf". /// /// @return FAIL for failure, OK otherwise. int ml_open(buf_T *buf) { - bhdr_T *hp = NULL; - // init fields in memline struct buf->b_ml.ml_stack_size = 0; // no stack yet buf->b_ml.ml_stack = NULL; // no stack yet @@ -265,14 +293,14 @@ int ml_open(buf_T *buf) buf->b_p_swf = false; } - // When 'updatecount' is non-zero swap file may be opened later. + // When 'updatecount' is non-zero swapfile may be opened later. if (!buf->terminal && p_uc && buf->b_p_swf) { buf->b_may_swap = true; } else { buf->b_may_swap = false; } - // Open the memfile. No swap file is created yet. + // Open the memfile. No swapfile is created yet. memfile_T *mfp = mf_open(NULL, 0); if (mfp == NULL) { goto error; @@ -283,12 +311,12 @@ int ml_open(buf_T *buf) buf->b_ml.ml_line_count = 1; // fill block0 struct and write page 0 - hp = mf_new(mfp, false, 1); + bhdr_T *hp = mf_new(mfp, false, 1); if (hp->bh_bnum != 0) { iemsg(_("E298: Didn't get block nr 0?")); goto error; } - ZERO_BL *b0p = hp->bh_data; + ZeroBlock *b0p = hp->bh_data; b0p->b0_id[0] = BLOCK0_ID0; b0p->b0_id[1] = BLOCK0_ID1; @@ -303,32 +331,31 @@ int ml_open(buf_T *buf) b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0; b0p->b0_flags = (char)(get_fileformat(buf) + 1); set_b0_fname(b0p, buf); - (void)os_get_username((char *)b0p->b0_uname, B0_UNAME_SIZE); + (void)os_get_username(b0p->b0_uname, B0_UNAME_SIZE); b0p->b0_uname[B0_UNAME_SIZE - 1] = NUL; - os_get_hostname((char *)b0p->b0_hname, B0_HNAME_SIZE); + os_get_hostname(b0p->b0_hname, B0_HNAME_SIZE); b0p->b0_hname[B0_HNAME_SIZE - 1] = NUL; - long_to_char(os_get_pid(), b0p->b0_pid); + long_to_char((long)os_get_pid(), b0p->b0_pid); } // Always sync block number 0 to disk, so we can check the file name in - // the swap file in findswapname(). Don't do this for a help files or + // the swapfile in findswapname(). Don't do this for a help files or // a spell buffer though. // Only works when there's a swapfile, otherwise it's done when the file // is created. mf_put(mfp, hp, true, false); - if (!buf->b_help && !B_SPELL(buf)) { + if (!buf->b_help && !buf->b_spell) { (void)mf_sync(mfp, 0); } // Fill in root pointer block and write page 1. - if ((hp = ml_new_ptr(mfp)) == NULL) { - goto error; - } + hp = ml_new_ptr(mfp); + assert(hp != NULL); if (hp->bh_bnum != 1) { iemsg(_("E298: Didn't get block nr 1?")); goto error; } - PTR_BL *pp = hp->bh_data; + PointerBlock *pp = hp->bh_data; pp->pb_count = 1; pp->pb_pointer[0].pe_bnum = 2; pp->pb_pointer[0].pe_page_count = 1; @@ -343,11 +370,11 @@ int ml_open(buf_T *buf) goto error; } - DATA_BL *dp = hp->bh_data; + DataBlock *dp = hp->bh_data; dp->db_index[0] = --dp->db_txt_start; // at end of block dp->db_free -= 1 + (unsigned)INDEX_SIZE; dp->db_line_count = 1; - *((char_u *)dp + dp->db_txt_start) = NUL; // empty line + *((char *)dp + dp->db_txt_start) = NUL; // empty line return OK; @@ -363,17 +390,17 @@ error: } /// ml_setname() is called when the file name of "buf" has been changed. -/// It may rename the swap file. +/// It may rename the swapfile. void ml_setname(buf_T *buf) { bool success = false; memfile_T *mfp = buf->b_ml.ml_mfp; - if (mfp->mf_fd < 0) { // there is no swap file yet - // When 'updatecount' is 0 and 'noswapfile' there is no swap file. - // For help files we will make a swap file now. + if (mfp->mf_fd < 0) { // there is no swapfile yet + // When 'updatecount' is 0 and 'noswapfile' there is no swapfile. + // For help files we will make a swapfile now. if (p_uc != 0 && (cmdmod.cmod_flags & CMOD_NOSWAPFILE) == 0) { - ml_open_file(buf); // create a swap file + ml_open_file(buf); // create a swapfile } return; } @@ -381,7 +408,7 @@ void ml_setname(buf_T *buf) // Try all directories in the 'directory' option. char *dirp = p_dir; bool found_existing_dir = false; - for (;;) { + while (true) { if (*dirp == NUL) { // tried all directories, fail break; } @@ -400,13 +427,13 @@ void ml_setname(buf_T *buf) success = true; break; } - // need to close the swap file before renaming + // need to close the swapfile before renaming if (mfp->mf_fd >= 0) { close(mfp->mf_fd); mfp->mf_fd = -1; } - // try to rename the swap file + // try to rename the swapfile if (vim_rename(mfp->mf_fname, fname) == 0) { success = true; mf_free_fnames(mfp); @@ -417,10 +444,10 @@ void ml_setname(buf_T *buf) xfree(fname); // this fname didn't work, try another } - if (mfp->mf_fd == -1) { // need to (re)open the swap file + if (mfp->mf_fd == -1) { // need to (re)open the swapfile mfp->mf_fd = os_open(mfp->mf_fname, O_RDWR, 0); if (mfp->mf_fd < 0) { - // could not (re)open the swap file, what can we do???? + // could not (re)open the swapfile, what can we do???? emsg(_("E301: Oops, lost the swap file!!!")); return; } @@ -443,7 +470,7 @@ void ml_open_files(void) } } -/// Open a swap file for an existing memfile, if there is no swap file yet. +/// Open a swapfile for an existing memfile, if there is no swapfile yet. /// If we are unable to find a file name, mf_fname will be NULL /// and the memfile will be in memory only (no recovery possible). void ml_open_file(buf_T *buf) @@ -468,11 +495,11 @@ void ml_open_file(buf_T *buf) // Try all directories in 'directory' option. char *dirp = p_dir; bool found_existing_dir = false; - for (;;) { + while (true) { if (*dirp == NUL) { break; } - // There is a small chance that between choosing the swap file name + // There is a small chance that between choosing the swapfile name // and creating it, another Vim creates the file. In that case the // creation will fail and we will use another directory. char *fname = findswapname(buf, &dirp, NULL, &found_existing_dir); @@ -483,13 +510,15 @@ void ml_open_file(buf_T *buf) continue; } if (mf_open_file(mfp, fname) == OK) { // consumes fname! + // don't sync yet in ml_sync_all() + mfp->mf_dirty = MF_DIRTY_YES_NOSYNC; ml_upd_block0(buf, UB_SAME_DIR); // Flush block zero, so others can read it if (mf_sync(mfp, MFS_ZERO) == OK) { // Mark all blocks that should be in the swapfile as dirty. // Needed for when the 'swapfile' option was reset, so that - // the swap file was deleted, and then on again. + // the swapfile was deleted, and then on again. mf_set_dirty(mfp); break; } @@ -506,12 +535,12 @@ void ml_open_file(buf_T *buf) no_wait_return--; } - // don't try to open a swap file again + // don't try to open a swapfile again buf->b_may_swap = false; } -/// If still need to create a swap file, and starting to edit a not-readonly -/// file, or reading into an existing buffer, create a swap file now. +/// If still need to create a swapfile, and starting to edit a not-readonly +/// file, or reading into an existing buffer, create a swapfile now. /// /// @param newfile reading file into new buffer void check_need_swap(bool newfile) @@ -528,14 +557,15 @@ void check_need_swap(bool newfile) /// Close memline for buffer 'buf'. /// -/// @param del_file if true, delete the swap file +/// @param del_file if true, delete the swapfile void ml_close(buf_T *buf, int del_file) { if (buf->b_ml.ml_mfp == NULL) { // not open return; } mf_close(buf->b_ml.ml_mfp, del_file); // close the .swp file - if (buf->b_ml.ml_line_lnum != 0 && (buf->b_ml.ml_flags & ML_LINE_DIRTY)) { + if (buf->b_ml.ml_line_lnum != 0 + && (buf->b_ml.ml_flags & (ML_LINE_DIRTY | ML_ALLOCATED))) { xfree(buf->b_ml.ml_line_ptr); } xfree(buf->b_ml.ml_stack); @@ -579,20 +609,20 @@ void ml_timestamp(buf_T *buf) } /// Checks whether the IDs in b0 are valid. -static bool ml_check_b0_id(ZERO_BL *b0p) +static bool ml_check_b0_id(ZeroBlock *b0p) FUNC_ATTR_NONNULL_ALL { return b0p->b0_id[0] == BLOCK0_ID0 && b0p->b0_id[1] == BLOCK0_ID1; } /// Checks whether all strings in b0 are valid (i.e. nul-terminated). -static bool ml_check_b0_strings(ZERO_BL *b0p) +static bool ml_check_b0_strings(ZeroBlock *b0p) FUNC_ATTR_NONNULL_ALL { return (memchr(b0p->b0_version, NUL, 10) && memchr(b0p->b0_uname, NUL, B0_UNAME_SIZE) && memchr(b0p->b0_hname, NUL, B0_HNAME_SIZE) - && memchr(b0p->b0_fname, NUL, B0_FNAME_SIZE_CRYPT)); // -V1086 + && memchr(b0p->b0_fname, NUL, B0_FNAME_SIZE_CRYPT)); } /// Update the timestamp or the B0_SAME_DIR flag of the .swp file. @@ -604,7 +634,7 @@ static void ml_upd_block0(buf_T *buf, upd_block0_T what) if (mfp == NULL || (hp = mf_get(mfp, 0, 1)) == NULL) { return; } - ZERO_BL *b0p = hp->bh_data; + ZeroBlock *b0p = hp->bh_data; if (ml_check_b0_id(b0p) == FAIL) { iemsg(_("E304: ml_upd_block0(): Didn't get block 0??")); } else { @@ -617,10 +647,10 @@ static void ml_upd_block0(buf_T *buf, upd_block0_T what) mf_put(mfp, hp, true, false); } -/// Write file name and timestamp into block 0 of a swap file. +/// Write file name and timestamp into block 0 of a swapfile. /// Also set buf->b_mtime. /// Don't use NameBuff[]!!! -static void set_b0_fname(ZERO_BL *b0p, buf_T *buf) +static void set_b0_fname(ZeroBlock *b0p, buf_T *buf) { if (buf->b_ffname == NULL) { b0p->b0_fname[0] = NUL; @@ -632,7 +662,7 @@ static void set_b0_fname(ZERO_BL *b0p, buf_T *buf) // editing the same file on different machines over a network. // First replace home dir path with "~/" with home_replace(). // Then insert the user name to get "~user/". - home_replace(NULL, buf->b_ffname, (char *)b0p->b0_fname, + home_replace(NULL, buf->b_ffname, b0p->b0_fname, B0_FNAME_SIZE_CRYPT, true); if (b0p->b0_fname[0] == '~') { // If there is no user name or it is too long, don't use "~/" @@ -654,8 +684,8 @@ static void set_b0_fname(ZERO_BL *b0p, buf_T *buf) buf->b_mtime_read = buf->b_mtime; buf->b_mtime_read_ns = buf->b_mtime_ns; } else { - long_to_char(0L, b0p->b0_mtime); - long_to_char(0L, b0p->b0_ino); + long_to_char(0, b0p->b0_mtime); + long_to_char(0, b0p->b0_ino); buf->b_mtime = 0; buf->b_mtime_ns = 0; buf->b_mtime_read = 0; @@ -669,11 +699,11 @@ static void set_b0_fname(ZERO_BL *b0p, buf_T *buf) add_b0_fenc(b0p, curbuf); } -/// Update the B0_SAME_DIR flag of the swap file. It's set if the file and the +/// Update the B0_SAME_DIR flag of the swapfile. It's set if the file and the /// swapfile for "buf" are in the same directory. /// This is fail safe: if we are not sure the directories are equal the flag is /// not set. -static void set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf) +static void set_b0_dir_flag(ZeroBlock *b0p, buf_T *buf) { if (same_directory(buf->b_ml.ml_mfp->mf_fname, buf->b_ffname)) { b0p->b0_flags |= B0_SAME_DIR; @@ -683,7 +713,7 @@ static void set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf) } /// When there is room, add the 'fileencoding' to block zero. -static void add_b0_fenc(ZERO_BL *b0p, buf_T *buf) +static void add_b0_fenc(ZeroBlock *b0p, buf_T *buf) { const int size = B0_FNAME_SIZE_NOCRYPT; @@ -691,48 +721,46 @@ static void add_b0_fenc(ZERO_BL *b0p, buf_T *buf) if ((int)strlen(b0p->b0_fname) + n + 1 > size) { b0p->b0_flags = (char)(b0p->b0_flags & ~B0_HAS_FENC); } else { - memmove((char *)b0p->b0_fname + size - n, + memmove(b0p->b0_fname + size - n, buf->b_p_fenc, (size_t)n); *(b0p->b0_fname + size - n - 1) = NUL; b0p->b0_flags |= B0_HAS_FENC; } } -/// Return true if the process with number "b0p->b0_pid" is still running. -/// "swap_fname" is the name of the swap file, if it's from before a reboot then -/// the result is false; -static bool swapfile_process_running(const ZERO_BL *b0p, const char *swap_fname) +/// Returns the PID of the process that owns the swapfile, if it is running. +/// +/// @param b0p swapfile data +/// @param swap_fname Name of the swapfile. If it's from before a reboot, the result is 0. +/// +/// @return PID, or 0 if process is not running or the swapfile is from before a reboot. +static int swapfile_process_running(const ZeroBlock *b0p, const char *swap_fname) { FileInfo st; double uptime; - // If the system rebooted after when the swap file was written then the + // If the system rebooted after when the swapfile was written then the // process can't be running now. if (os_fileinfo(swap_fname, &st) && uv_uptime(&uptime) == 0 && (Timestamp)st.stat.st_mtim.tv_sec < os_time() - (Timestamp)uptime) { - return false; + return 0; } - return os_proc_running((int)char_to_long(b0p->b0_pid)); + int pid = (int)char_to_long(b0p->b0_pid); + return os_proc_running(pid) ? pid : 0; } /// Try to recover curbuf from the .swp file. /// -/// @param checkext if true, check the extension and detect whether it is a -/// swap file. +/// @param checkext if true, check the extension and detect whether it is a swapfile. void ml_recover(bool checkext) { buf_T *buf = NULL; memfile_T *mfp = NULL; char *fname_used = NULL; bhdr_T *hp = NULL; - ZERO_BL *b0p; - int b0_ff; char *b0_fenc = NULL; - PTR_BL *pp; - DATA_BL *dp; infoptr_T *ip; bool directly; - char *p; bool serious_error = true; int orig_file_status = NOTDONE; @@ -740,8 +768,8 @@ void ml_recover(bool checkext) int called_from_main = (curbuf->b_ml.ml_mfp == NULL); int attr = HL_ATTR(HLF_E); - // If the file name ends in ".s[a-w][a-z]" we assume this is the swap file. - // Otherwise a search is done to find the swap file(s). + // If the file name ends in ".s[a-w][a-z]" we assume this is the swapfile. + // Otherwise a search is done to find the swapfile(s). char *fname = curbuf->b_fname; if (fname == NULL) { // When there is no file name fname = ""; @@ -756,18 +784,18 @@ void ml_recover(bool checkext) } else { directly = false; - // count the number of matching swap files - len = recover_names(fname, false, 0, NULL); - if (len == 0) { // no swap files found + // count the number of matching swapfiles + len = recover_names(fname, false, NULL, 0, NULL); + if (len == 0) { // no swapfiles found semsg(_("E305: No swap file found for %s"), fname); goto theend; } int i; - if (len == 1) { // one swap file found, use it + if (len == 1) { // one swapfile found, use it i = 1; - } else { // several swap files found, choose - // list the names of the swap files - (void)recover_names(fname, true, 0, NULL); + } else { // several swapfiles found, choose + // list the names of the swapfiles + (void)recover_names(fname, true, NULL, 0, NULL); msg_putchar('\n'); msg_puts(_("Enter number of swap file to use (0 to quit): ")); i = get_number(false, NULL); @@ -775,8 +803,8 @@ void ml_recover(bool checkext) goto theend; } } - // get the swap file name that will be used - (void)recover_names(fname, false, i, &fname_used); + // get the swapfile name that will be used + (void)recover_names(fname, false, NULL, i, &fname_used); } if (fname_used == NULL) { goto theend; // user chose invalid number. @@ -786,7 +814,7 @@ void ml_recover(bool checkext) getout(1); } - // Allocate a buffer structure for the swap file that is used for recovery. + // Allocate a buffer structure for the swapfile that is used for recovery. // Only the memline in it is really used. buf = xmalloc(sizeof(buf_T)); @@ -799,8 +827,8 @@ void ml_recover(bool checkext) buf->b_ml.ml_locked = NULL; // no locked block buf->b_ml.ml_flags = 0; - // open the memfile from the old swap file - p = xstrdup(fname_used); // save "fname_used" for the message: + // open the memfile from the old swapfile + char *p = xstrdup(fname_used); // save "fname_used" for the message: // mf_open() will consume "fname_used"! mfp = mf_open(fname_used, O_RDONLY); fname_used = p; @@ -811,7 +839,7 @@ void ml_recover(bool checkext) buf->b_ml.ml_mfp = mfp; // The page size set in mf_open() might be different from the page size - // used in the swap file, we must get it from block 0. But to read block + // used in the swapfile, we must get it from block 0. But to read block // 0 we need a page size. Use the minimal size for block 0 here, it will // be set to the real value below. mfp->mf_page_size = MIN_SWAP_PAGE_SIZE; @@ -820,16 +848,16 @@ void ml_recover(bool checkext) if ((hp = mf_get(mfp, 0, 1)) == NULL) { msg_start(); msg_puts_attr(_("Unable to read block 0 from "), attr | MSG_HIST); - msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST); + msg_outtrans(mfp->mf_fname, attr | MSG_HIST); msg_puts_attr(_("\nMaybe no changes were made or Vim did not update the swap file."), attr | MSG_HIST); msg_end(); goto theend; } - b0p = hp->bh_data; + ZeroBlock *b0p = hp->bh_data; if (strncmp(b0p->b0_version, "VIM 3.0", 7) == 0) { msg_start(); - msg_outtrans_attr(mfp->mf_fname, MSG_HIST); + msg_outtrans(mfp->mf_fname, MSG_HIST); msg_puts_attr(_(" cannot be used with this version of Vim.\n"), MSG_HIST); msg_puts_attr(_("Use Vim version 3.0.\n"), MSG_HIST); @@ -842,13 +870,13 @@ void ml_recover(bool checkext) } if (b0_magic_wrong(b0p)) { msg_start(); - msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST); + msg_outtrans(mfp->mf_fname, attr | MSG_HIST); msg_puts_attr(_(" cannot be used on this computer.\n"), attr | MSG_HIST); msg_puts_attr(_("The file was created on "), attr | MSG_HIST); // avoid going past the end of a corrupted hostname b0p->b0_fname[0] = NUL; - msg_puts_attr((char *)b0p->b0_hname, attr | MSG_HIST); + msg_puts_attr(b0p->b0_hname, attr | MSG_HIST); msg_puts_attr(_(",\nor the file has been damaged."), attr | MSG_HIST); msg_end(); goto theend; @@ -862,14 +890,14 @@ void ml_recover(bool checkext) mf_new_page_size(mfp, (unsigned)char_to_long(b0p->b0_page_size)); if (mfp->mf_page_size < previous_page_size) { msg_start(); - msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST); + msg_outtrans(mfp->mf_fname, attr | MSG_HIST); msg_puts_attr(_(" has been damaged (page size is smaller than minimum value).\n"), attr | MSG_HIST); msg_end(); goto theend; } off_T size; - if ((size = vim_lseek(mfp->mf_fd, (off_T)0L, SEEK_END)) <= 0) { + if ((size = vim_lseek(mfp->mf_fd, 0, SEEK_END)) <= 0) { mfp->mf_blocknr_max = 0; // no file or empty file } else { mfp->mf_blocknr_max = size / mfp->mf_page_size; @@ -884,29 +912,29 @@ void ml_recover(bool checkext) b0p = hp->bh_data; } - // If .swp file name given directly, use name from swap file for buffer. + // If .swp file name given directly, use name from swapfile for buffer. if (directly) { - expand_env((char *)b0p->b0_fname, NameBuff, MAXPATHL); + expand_env(b0p->b0_fname, NameBuff, MAXPATHL); if (setfname(curbuf, NameBuff, NULL, true) == FAIL) { goto theend; } } home_replace(NULL, mfp->mf_fname, NameBuff, MAXPATHL, true); - smsg(_("Using swap file \"%s\""), NameBuff); + smsg(0, _("Using swap file \"%s\""), NameBuff); if (buf_spname(curbuf) != NULL) { xstrlcpy(NameBuff, buf_spname(curbuf), MAXPATHL); } else { home_replace(NULL, curbuf->b_ffname, NameBuff, MAXPATHL, true); } - smsg(_("Original file \"%s\""), NameBuff); + smsg(0, _("Original file \"%s\""), NameBuff); msg_putchar('\n'); - // check date of swap file and original file + // check date of swapfile and original file FileInfo org_file_info; FileInfo swp_file_info; - long mtime = char_to_long(b0p->b0_mtime); + int mtime = (int)char_to_long(b0p->b0_mtime); if (curbuf->b_ffname != NULL && os_fileinfo(curbuf->b_ffname, &org_file_info) && ((os_fileinfo(mfp->mf_fname, &swp_file_info) @@ -918,11 +946,11 @@ void ml_recover(bool checkext) ui_flush(); // Get the 'fileformat' and 'fileencoding' from block zero. - b0_ff = (b0p->b0_flags & B0_FF_MASK); + int b0_ff = (b0p->b0_flags & B0_FF_MASK); if (b0p->b0_flags & B0_HAS_FENC) { int fnsize = B0_FNAME_SIZE_NOCRYPT; - for (p = (char *)b0p->b0_fname + fnsize; p > b0p->b0_fname && p[-1] != NUL; p--) {} + for (p = b0p->b0_fname + fnsize; p > b0p->b0_fname && p[-1] != NUL; p--) {} b0_fenc = xstrnsave(p, (size_t)(b0p->b0_fname + fnsize - p)); } @@ -932,22 +960,22 @@ void ml_recover(bool checkext) // Now that we are sure that the file is going to be recovered, clear the // contents of the current buffer. while (!(curbuf->b_ml.ml_flags & ML_EMPTY)) { - ml_delete((linenr_T)1, false); + ml_delete(1, false); } // Try reading the original file to obtain the values of 'fileformat', // 'fileencoding', etc. Ignore errors. The text itself is not used. if (curbuf->b_ffname != NULL) { - orig_file_status = readfile(curbuf->b_ffname, NULL, (linenr_T)0, - (linenr_T)0, (linenr_T)MAXLNUM, NULL, READ_NEW, false); + orig_file_status = readfile(curbuf->b_ffname, NULL, 0, + 0, MAXLNUM, NULL, READ_NEW, false); } - // Use the 'fileformat' and 'fileencoding' as stored in the swap file. + // Use the 'fileformat' and 'fileencoding' as stored in the swapfile. if (b0_ff != 0) { set_fileformat(b0_ff - 1, OPT_LOCAL); } if (b0_fenc != NULL) { - set_option_value_give_err("fenc", 0L, b0_fenc, OPT_LOCAL); + set_option_value_give_err("fenc", CSTR_AS_OPTVAL(b0_fenc), OPT_LOCAL); xfree(b0_fenc); } unchanged(curbuf, true, true); @@ -957,10 +985,10 @@ void ml_recover(bool checkext) linenr_T lnum = 0; // append after line 0 in curbuf linenr_T line_count = 0; int idx = 0; // start with first index in block 1 - long error = 0; - buf->b_ml.ml_stack_top = 0; // -V1048 + int error = 0; + buf->b_ml.ml_stack_top = 0; buf->b_ml.ml_stack = NULL; - buf->b_ml.ml_stack_size = 0; // -V1048 + buf->b_ml.ml_stack_size = 0; bool cannot_open = (curbuf->b_ffname == NULL); @@ -976,11 +1004,23 @@ void ml_recover(bool checkext) goto theend; } error++; - ml_append(lnum++, _("???MANY LINES MISSING"), - (colnr_T)0, true); + ml_append(lnum++, _("???MANY LINES MISSING"), 0, true); } else { // there is a block - pp = hp->bh_data; + PointerBlock *pp = hp->bh_data; if (pp->pb_id == PTR_ID) { // it is a pointer block + bool ptr_block_error = false; + if (pp->pb_count_max != PB_COUNT_MAX(mfp)) { + ptr_block_error = true; + pp->pb_count_max = PB_COUNT_MAX(mfp); + } + if (pp->pb_count > pp->pb_count_max) { + ptr_block_error = true; + pp->pb_count = pp->pb_count_max; + } + if (ptr_block_error) { + emsg(_(e_warning_pointer_block_corrupted)); + } + // check line count when using pointer block first time if (idx == 0 && line_count != 0) { for (int i = 0; i < (int)pp->pb_count; i++) { @@ -988,14 +1028,12 @@ void ml_recover(bool checkext) } if (line_count != 0) { error++; - ml_append(lnum++, _("???LINE COUNT WRONG"), - (colnr_T)0, true); + ml_append(lnum++, _("???LINE COUNT WRONG"), 0, true); } } if (pp->pb_count == 0) { - ml_append(lnum++, _("???EMPTY BLOCK"), - (colnr_T)0, true); + ml_append(lnum++, _("???EMPTY BLOCK"), 0, true); error++; } else if (idx < (int)pp->pb_count) { // go a block deeper if (pp->pb_pointer[idx].pe_bnum < 0) { @@ -1014,8 +1052,7 @@ void ml_recover(bool checkext) } if (cannot_open) { error++; - ml_append(lnum++, _("???LINES MISSING"), - (colnr_T)0, true); + ml_append(lnum++, _("???LINES MISSING"), 0, true); } idx++; // get same block again for next index continue; @@ -1034,7 +1071,7 @@ void ml_recover(bool checkext) continue; } } else { // not a pointer block - dp = hp->bh_data; + DataBlock *dp = hp->bh_data; if (dp->db_id != DATA_ID) { // block id wrong if (bnum == 1) { semsg(_("E310: Block 1 ID wrong (%s not a .swp file?)"), @@ -1042,50 +1079,65 @@ void ml_recover(bool checkext) goto theend; } error++; - ml_append(lnum++, _("???BLOCK MISSING"), - (colnr_T)0, true); + ml_append(lnum++, _("???BLOCK MISSING"), 0, true); } else { - // it is a data block - // Append all the lines in this block + // It is a data block. + // Append all the lines in this block. bool has_error = false; - // check length of block - // if wrong, use length in pointer block + + // Check the length of the block. + // If wrong, use the length given in the pointer block. if (page_count * mfp->mf_page_size != dp->db_txt_end) { ml_append(lnum++, _("??? from here until ???END lines" " may be messed up"), - (colnr_T)0, true); + 0, true); error++; has_error = true; dp->db_txt_end = page_count * mfp->mf_page_size; } - // make sure there is a NUL at the end of the block - *((char_u *)dp + dp->db_txt_end - 1) = NUL; + // Make sure there is a NUL at the end of the block so we + // don't go over the end when copying text. + *((char *)dp + dp->db_txt_end - 1) = NUL; - // check number of lines in block - // if wrong, use count in data block + // Check the number of lines in the block. + // If wrong, use the count in the data block. if (line_count != dp->db_line_count) { ml_append(lnum++, _("??? from here until ???END lines" " may have been inserted/deleted"), - (colnr_T)0, true); + 0, true); error++; has_error = true; } + bool did_questions = false; for (int i = 0; i < dp->db_line_count; i++) { + if ((char *)&(dp->db_index[i]) >= (char *)dp + dp->db_txt_start) { + // line count must be wrong + error++; + ml_append(lnum++, _("??? lines may be missing"), 0, true); + break; + } + int txt_start = (dp->db_index[i] & DB_INDEX_MASK); if (txt_start <= (int)HEADER_SIZE || txt_start >= (int)dp->db_txt_end) { - p = "???"; error++; + // avoid lots of lines with "???" + if (did_questions) { + continue; + } + did_questions = true; + p = "???"; } else { + did_questions = false; p = (char *)dp + txt_start; } - ml_append(lnum++, p, (colnr_T)0, true); + ml_append(lnum++, p, 0, true); } if (has_error) { - ml_append(lnum++, _("???END"), (colnr_T)0, true); + ml_append(lnum++, _("???END"), 0, true); } } } @@ -1111,7 +1163,7 @@ void ml_recover(bool checkext) // Recovering an empty file results in two lines and the first line is // empty. Don't set the modified flag then. if (!(curbuf->b_ml.ml_line_count == 2 && *ml_get(1) == NUL)) { - changed_internal(); + changed_internal(curbuf); buf_inc_changedtick(curbuf); } } else { @@ -1121,7 +1173,7 @@ void ml_recover(bool checkext) int i = strcmp(p, ml_get(idx + lnum)); xfree(p); if (i != 0) { - changed_internal(); + changed_internal(curbuf); buf_inc_changedtick(curbuf); break; } @@ -1142,25 +1194,25 @@ void ml_recover(bool checkext) emsg(_("E311: Recovery Interrupted")); } else if (error) { no_wait_return++; - msg(">>>>>>>>>>>>>"); + msg(">>>>>>>>>>>>>", 0); emsg(_("E312: Errors detected while recovering; look for lines starting with ???")); no_wait_return--; - msg(_("See \":help E312\" for more information.")); - msg(">>>>>>>>>>>>>"); + msg(_("See \":help E312\" for more information."), 0); + msg(">>>>>>>>>>>>>", 0); } else { if (curbuf->b_changed) { - msg(_("Recovery completed. You should check if everything is OK.")); + msg(_("Recovery completed. You should check if everything is OK."), 0); msg_puts(_("\n(You might want to write out this file under another name\n")); msg_puts(_("and run diff with the original file to check for changes)")); } else { - msg(_("Recovery completed. Buffer contents equals file contents.")); + msg(_("Recovery completed. Buffer contents equals file contents."), 0); } msg_puts(_("\nYou may want to delete the .swp file now.")); if (swapfile_process_running(b0p, fname_used)) { // Warn there could be an active Vim on the same file, the user may // want to kill it. msg_puts(_("\nNote: process STILL RUNNING: ")); - msg_outnum(char_to_long(b0p->b0_pid)); + msg_outnum((int)char_to_long(b0p->b0_pid)); } msg_puts("\n\n"); cmdline_row = msg_row; @@ -1176,7 +1228,7 @@ theend: } mf_close(mfp, false); // will also xfree(mfp->mf_fname) } - if (buf != NULL) { // may be NULL if swap file not found. + if (buf != NULL) { // may be NULL if swapfile not found. xfree(buf->b_ml.ml_stack); xfree(buf); } @@ -1188,20 +1240,22 @@ theend: } } -/// Find the names of swap files in current directory and the directory given +/// Find the names of swapfiles in current directory and the directory given /// with the 'directory' option. /// /// Used to: -/// - list the swap files for "vim -r" -/// - count the number of swap files when recovering -/// - list the swap files when recovering -/// - find the name of the n'th swap file when recovering -/// -/// @param fname base for swap file name -/// @param list when true, list the swap file names -/// @param nr when non-zero, return nr'th swap file name +/// - list the swapfiles for "vim -r" +/// - count the number of swapfiles when recovering +/// - list the swapfiles when recovering +/// - list the swapfiles for swapfilelist() +/// - find the name of the n'th swapfile when recovering +/// +/// @param fname base for swapfile name +/// @param do_list when true, list the swapfile names +/// @param ret_list when not NULL add file names to it +/// @param nr when non-zero, return nr'th swapfile name /// @param fname_out result when "nr" > 0 -int recover_names(char *fname, int list, int nr, char **fname_out) +int recover_names(char *fname, bool do_list, list_T *ret_list, int nr, char **fname_out) { int num_names; char *(names[6]); @@ -1216,18 +1270,17 @@ int recover_names(char *fname, int list, int nr, char **fname_out) if (fname != NULL) { #ifdef HAVE_READLINK - // Expand symlink in the file name, because the swap file is created + // Expand symlink in the file name, because the swapfile is created // with the actual file instead of with the symlink. - if (resolve_symlink(fname, fname_buf) == OK) { - fname_res = fname_buf; - } else -#endif + fname_res = (resolve_symlink(fname, fname_buf) == OK) ? fname_buf : fname; +#else fname_res = fname; +#endif } - if (list) { + if (do_list) { // use msg() to start the scrolling properly - msg(_("Swap files found:")); + msg(_("Swap files found:"), 0); msg_putchar('\n'); } @@ -1285,9 +1338,9 @@ int recover_names(char *fname, int list, int nr, char **fname_out) num_files = 0; } - // When no swap file found, wildcard expansion might have failed (e.g. + // When no swapfile found, wildcard expansion might have failed (e.g. // not able to execute the shell). - // Try finding a swap file by simply adding ".swp" to the file name. + // Try finding a swapfile by simply adding ".swp" to the file name. if (*dirp == NUL && file_count + num_files == 0 && fname != NULL) { char *swapname = modname(fname_res, ".swp", true); if (swapname != NULL) { @@ -1301,9 +1354,11 @@ int recover_names(char *fname, int list, int nr, char **fname_out) } } - // remove swapfile name of the current buffer, it must be ignored + // Remove swapfile name of the current buffer, it must be ignored. + // But keep it for swapfilelist(). if (curbuf->b_ml.ml_mfp != NULL - && (p = curbuf->b_ml.ml_mfp->mf_fname) != NULL) { + && (p = curbuf->b_ml.ml_mfp->mf_fname) != NULL + && ret_list == NULL) { for (int i = 0; i < num_files; i++) { // Do not expand wildcards, on Windows would try to expand // "%tmp%" in "%tmp%file" @@ -1328,7 +1383,7 @@ int recover_names(char *fname, int list, int nr, char **fname_out) *fname_out = xstrdup(files[nr - 1 + num_files - file_count]); dirp = ""; // stop searching } - } else if (list) { + } else if (do_list) { if (dir_name[0] == '.' && dir_name[1] == NUL) { if (fname == NULL) { msg_puts(_(" In current directory:\n")); @@ -1343,10 +1398,10 @@ int recover_names(char *fname, int list, int nr, char **fname_out) if (num_files) { for (int i = 0; i < num_files; i++) { - // print the swap file name - msg_outnum((long)++file_count); + // print the swapfile name + msg_outnum(++file_count); msg_puts(". "); - msg_puts((const char *)path_tail(files[i])); + msg_puts(path_tail(files[i])); msg_putchar('\n'); (void)swapfile_info(files[i]); } @@ -1354,6 +1409,11 @@ int recover_names(char *fname, int list, int nr, char **fname_out) msg_puts(_(" -- none --\n")); } ui_flush(); + } else if (ret_list != NULL) { + for (int i = 0; i < num_files; i++) { + char *name = concat_fnames(dir_name, files[i], true); + tv_list_append_allocated_string(ret_list, name); + } } else { file_count += num_files; } @@ -1392,15 +1452,16 @@ char *make_percent_swname(const char *dir, const char *name) return d; } -static bool process_still_running; +// PID of swapfile owner, or zero if not running. +static int process_running; -/// This is used by the swapinfo() function. +/// For Vimscript "swapinfo()". /// /// @return information found in swapfile "fname" in dictionary "d". -void get_b0_dict(const char *fname, dict_T *d) +void swapfile_dict(const char *fname, dict_T *d) { int fd; - struct block0 b0; + ZeroBlock b0; if ((fd = os_open(fname, O_RDONLY, 0)) >= 0) { if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0)) { @@ -1411,14 +1472,14 @@ void get_b0_dict(const char *fname, dict_T *d) } else { // We have swap information. tv_dict_add_str_len(d, S_LEN("version"), b0.b0_version, 10); - tv_dict_add_str_len(d, S_LEN("user"), (char *)b0.b0_uname, + tv_dict_add_str_len(d, S_LEN("user"), b0.b0_uname, B0_UNAME_SIZE); - tv_dict_add_str_len(d, S_LEN("host"), (char *)b0.b0_hname, + tv_dict_add_str_len(d, S_LEN("host"), b0.b0_hname, B0_HNAME_SIZE); - tv_dict_add_str_len(d, S_LEN("fname"), (char *)b0.b0_fname, + tv_dict_add_str_len(d, S_LEN("fname"), b0.b0_fname, B0_FNAME_SIZE_ORG); - tv_dict_add_nr(d, S_LEN("pid"), char_to_long(b0.b0_pid)); + tv_dict_add_nr(d, S_LEN("pid"), swapfile_process_running(&b0, fname)); tv_dict_add_nr(d, S_LEN("mtime"), char_to_long(b0.b0_mtime)); tv_dict_add_nr(d, S_LEN("dirty"), b0.b0_dirty ? 1 : 0); tv_dict_add_nr(d, S_LEN("inode"), char_to_long(b0.b0_ino)); @@ -1432,27 +1493,26 @@ void get_b0_dict(const char *fname, dict_T *d) } } -/// Give information about an existing swap file. +/// Loads info from swapfile `fname`, and displays it to the user. /// /// @return timestamp (0 when unknown). static time_t swapfile_info(char *fname) { assert(fname != NULL); - int fd; - struct block0 b0; + ZeroBlock b0; time_t x = (time_t)0; #ifdef UNIX char uname[B0_UNAME_SIZE]; #endif - // print the swap file date + // print the swapfile date FileInfo file_info; if (os_fileinfo(fname, &file_info)) { #ifdef UNIX // print name of owner of the file if (os_get_uname((uv_uid_t)file_info.stat.st_uid, uname, B0_UNAME_SIZE) == OK) { msg_puts(_(" owned by: ")); - msg_outtrans(uname); + msg_outtrans(uname, 0); msg_puts(_(" dated: ")); } else { msg_puts(_(" dated: ")); @@ -1466,7 +1526,7 @@ static time_t swapfile_info(char *fname) } // print the original file name - fd = os_open(fname, O_RDONLY, 0); + int fd = os_open(fname, O_RDONLY, 0); if (fd >= 0) { if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0)) { if (strncmp(b0.b0_version, "VIM 3.0", 7) == 0) { @@ -1480,7 +1540,7 @@ static time_t swapfile_info(char *fname) if (b0.b0_fname[0] == NUL) { msg_puts(_("[No Name]")); } else { - msg_outtrans((char *)b0.b0_fname); + msg_outtrans(b0.b0_fname, 0); } msg_puts(_("\n modified: ")); @@ -1488,7 +1548,7 @@ static time_t swapfile_info(char *fname) if (*(b0.b0_uname) != NUL) { msg_puts(_("\n user name: ")); - msg_outtrans((char *)b0.b0_uname); + msg_outtrans(b0.b0_uname, 0); } if (*(b0.b0_hname) != NUL) { @@ -1497,15 +1557,14 @@ static time_t swapfile_info(char *fname) } else { msg_puts(_("\n host name: ")); } - msg_outtrans((char *)b0.b0_hname); + msg_outtrans(b0.b0_hname, 0); } - if (char_to_long(b0.b0_pid) != 0L) { + if (char_to_long(b0.b0_pid) != 0) { msg_puts(_("\n process ID: ")); - msg_outnum(char_to_long(b0.b0_pid)); - if (swapfile_process_running(&b0, (const char *)fname)) { + msg_outnum((int)char_to_long(b0.b0_pid)); + if ((process_running = swapfile_process_running(&b0, fname))) { msg_puts(_(" (STILL RUNNING)")); - process_still_running = true; } } @@ -1525,13 +1584,12 @@ static time_t swapfile_info(char *fname) return x; } -/// @return true if the swap file looks OK and there are no changes, thus it -/// can be safely deleted. +/// @return true if the swapfile looks OK and there are no changes, thus it can be safely deleted. static bool swapfile_unchanged(char *fname) { - struct block0 b0; + ZeroBlock b0; - // Swap file must exist. + // Swapfile must exist. if (!os_path_exists(fname)) { return false; } @@ -1573,7 +1631,7 @@ static bool swapfile_unchanged(char *fname) } // process must be known and not running. - if (char_to_long(b0.b0_pid) == 0L || swapfile_process_running(&b0, fname)) { + if (char_to_long(b0.b0_pid) == 0 || swapfile_process_running(&b0, fname)) { ret = false; } @@ -1589,7 +1647,7 @@ static int recov_file_names(char **names, char *path, int prepend_dot) { int num_names = 0; - // May also add the file name with a dot prepended, for swap file in same + // May also add the file name with a dot prepended, for swapfile in same // dir as original file. if (prepend_dot) { names[num_names] = modname(path, ".sw?", true); @@ -1599,7 +1657,7 @@ static int recov_file_names(char **names, char *path, int prepend_dot) num_names++; } - // Form the normal swap file name pattern by appending ".sw?". + // Form the normal swapfile name pattern by appending ".sw?". names[num_names] = concat_fnames(path, ".sw?", false); if (num_names >= 1) { // check if we have the same name twice char *p = names[num_names - 1]; @@ -1633,7 +1691,7 @@ void ml_sync_all(int check_file, int check_char, bool do_fsync) } ml_flush_line(buf); // flush buffered line // flush locked block - (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); + (void)ml_find_line(buf, 0, ML_FLUSH); if (bufIsChanged(buf) && check_file && mf_need_trans(buf->b_ml.ml_mfp) && buf->b_ffname != NULL) { // If the original file does not exist anymore or has been changed @@ -1648,7 +1706,7 @@ void ml_sync_all(int check_file, int check_char, bool do_fsync) need_check_timestamps = true; // give message later } } - if (buf->b_ml.ml_mfp->mf_dirty) { + if (buf->b_ml.ml_mfp->mf_dirty == MF_DIRTY_YES) { (void)mf_sync(buf->b_ml.ml_mfp, (check_char ? MFS_STOP : 0) | (do_fsync && bufIsChanged(buf) ? MFS_FLUSH : 0)); if (check_char && os_char_avail()) { // character available now @@ -1660,7 +1718,7 @@ void ml_sync_all(int check_file, int check_char, bool do_fsync) /// sync one buffer, including negative blocks /// -/// after this all the blocks are in the swap file +/// after this all the blocks are in the swapfile /// /// Used for the :preserve command and when the original file has been /// changed or deleted. @@ -1683,7 +1741,7 @@ void ml_preserve(buf_T *buf, int message, bool do_fsync) got_int = false; ml_flush_line(buf); // flush buffered line - (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush locked block + (void)ml_find_line(buf, 0, ML_FLUSH); // flush locked block int status = mf_sync(mfp, MFS_ALL | (do_fsync ? MFS_FLUSH : 0)); // stack is invalid after mf_sync(.., MFS_ALL) @@ -1710,7 +1768,7 @@ void ml_preserve(buf_T *buf, int message, bool do_fsync) CHECK(buf->b_ml.ml_locked_low != lnum, "low != lnum"); lnum = buf->b_ml.ml_locked_high + 1; } - (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush locked block + (void)ml_find_line(buf, 0, ML_FLUSH); // flush locked block // sync the updated pointer blocks if (mf_sync(mfp, MFS_ALL | (do_fsync ? MFS_FLUSH : 0)) == FAIL) { status = FAIL; @@ -1722,7 +1780,7 @@ theend: if (message) { if (status == OK) { - msg(_("File preserved")); + msg(_("File preserved"), 0); } else { emsg(_("E314: Preserve failed")); } @@ -1735,20 +1793,40 @@ theend: // line2 = ml_get(2); // line1 is now invalid! // Make a copy of the line if necessary. -/// @return a pointer to a (read-only copy of a) line. +/// @return a pointer to a (read-only copy of a) line in curbuf. /// /// On failure an error message is given and IObuff is returned (to avoid /// having to check for error everywhere). char *ml_get(linenr_T lnum) { - return ml_get_buf(curbuf, lnum, false); + return ml_get_buf_impl(curbuf, lnum, false); +} + +/// @return a pointer to a (read-only copy of a) line. +/// +/// This is the same as ml_get(), but taking in the buffer +/// as an argument. +char *ml_get_buf(buf_T *buf, linenr_T lnum) +{ + return ml_get_buf_impl(buf, lnum, false); +} + +/// Like `ml_get_buf`, but allow the line to be mutated in place. +/// +/// This is very limited. Generally ml_replace_buf() +/// should be used to modify a line. +/// +/// @return a pointer to a line in the buffer +char *ml_get_buf_mut(buf_T *buf, linenr_T lnum) +{ + return ml_get_buf_impl(buf, lnum, true); } /// @return pointer to position "pos". char *ml_get_pos(const pos_T *pos) FUNC_ATTR_NONNULL_ALL { - return ml_get_buf(curbuf, pos->lnum, false) + pos->col; + return ml_get_buf(curbuf, pos->lnum) + pos->col; } /// @return codepoint at pos. pos must be either valid or have col set to MAXCOL! @@ -1765,7 +1843,7 @@ int gchar_pos(pos_T *pos) /// @param will_change true mark the buffer dirty (chars in the line will be changed) /// /// @return a pointer to a line in a specific buffer -char *ml_get_buf(buf_T *buf, linenr_T lnum, bool will_change) +static char *ml_get_buf_impl(buf_T *buf, linenr_T lnum, bool will_change) FUNC_ATTR_NONNULL_ALL { static int recursive = 0; @@ -1776,11 +1854,10 @@ char *ml_get_buf(buf_T *buf, linenr_T lnum, bool will_change) // Avoid giving this message for a recursive call, may happen when // the GUI redraws part of the text. recursive++; - siemsg(_("E315: ml_get: invalid lnum: %" PRId64), (int64_t)lnum); + siemsg(_(e_ml_get_invalid_lnum_nr), (int64_t)lnum); recursive--; } ml_flush_line(buf); - buf->b_ml.ml_flags &= ~ML_LINE_DIRTY; errorret: STRCPY(questions, "???"); buf->b_ml.ml_line_lnum = lnum; @@ -1812,30 +1889,48 @@ errorret: recursive++; get_trans_bufname(buf); shorten_dir(NameBuff); - siemsg(_("E316: ml_get: cannot find line %" PRId64 " in buffer %d %s"), + siemsg(_(e_ml_get_cannot_find_line_nr_in_buffer_nr_str), (int64_t)lnum, buf->b_fnum, NameBuff); recursive--; } goto errorret; } - DATA_BL *dp = hp->bh_data; + DataBlock *dp = hp->bh_data; char *ptr = (char *)dp + (dp->db_index[lnum - buf->b_ml.ml_locked_low] & DB_INDEX_MASK); buf->b_ml.ml_line_ptr = ptr; buf->b_ml.ml_line_lnum = lnum; - buf->b_ml.ml_flags &= ~ML_LINE_DIRTY; + buf->b_ml.ml_flags &= ~(ML_LINE_DIRTY | ML_ALLOCATED); } if (will_change) { buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS); +#ifdef ML_GET_ALLOC_LINES + if (buf->b_ml.ml_flags & ML_ALLOCATED) { + // can't make the change in the data block + buf->b_ml.ml_flags |= ML_LINE_DIRTY; + } +#endif ml_add_deleted_len_buf(buf, buf->b_ml.ml_line_ptr, -1); } +#ifdef ML_GET_ALLOC_LINES + if ((buf->b_ml.ml_flags & (ML_LINE_DIRTY | ML_ALLOCATED)) == 0) { + // make sure the text is in allocated memory + buf->b_ml.ml_line_ptr = xstrdup(buf->b_ml.ml_line_ptr); + buf->b_ml.ml_flags |= ML_ALLOCATED; + if (will_change) { + // can't make the change in the data block + buf->b_ml.ml_flags |= ML_LINE_DIRTY; + } + } +#endif return buf->b_ml.ml_line_ptr; } /// Check if a line that was just obtained by a call to ml_get /// is in allocated memory. +/// This ignores ML_ALLOCATED to get the same behavior as without ML_GET_ALLOC_LINES. int ml_line_alloced(void) { return curbuf->b_ml.ml_flags & ML_LINE_DIRTY; @@ -1917,7 +2012,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char *line, colnr_T len, boo // This also fills the stack with the blocks from the root to the data block // This also releases any locked block. bhdr_T *hp; - if ((hp = ml_find_line(buf, lnum == 0 ? (linenr_T)1 : lnum, + if ((hp = ml_find_line(buf, lnum == 0 ? 1 : lnum, ML_INSERT)) == NULL) { return FAIL; } @@ -1933,7 +2028,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char *line, colnr_T len, boo // get line count (number of indexes in current block) before the insertion int line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low; - DATA_BL *dp = hp->bh_data; + DataBlock *dp = hp->bh_data; // If // - there is not enough room in the current block @@ -2016,14 +2111,10 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char *line, colnr_T len, boo int lines_moved; int data_moved = 0; // init to shut up gcc int total_moved = 0; // init to shut up gcc - DATA_BL *dp_right, *dp_left; int stack_idx; bool in_left; - int lineadd; - blocknr_T bnum_left, bnum_right; linenr_T lnum_left, lnum_right; - int pb_idx; - PTR_BL *pp_new; + PointerBlock *pp_new; // We are going to allocate a new data block. Depending on the // situation it will be put to the left or right of the existing @@ -2067,10 +2158,10 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char *line, colnr_T len, boo line_count_left = line_count; line_count_right = 0; } - dp_right = hp_right->bh_data; - dp_left = hp_left->bh_data; - bnum_left = hp_left->bh_bnum; - bnum_right = hp_right->bh_bnum; + DataBlock *dp_right = hp_right->bh_data; + DataBlock *dp_left = hp_left->bh_data; + blocknr_T bnum_left = hp_left->bh_bnum; + blocknr_T bnum_right = hp_right->bh_bnum; page_count_left = (int)hp_left->bh_page_count; page_count_right = (int)hp_right->bh_page_count; @@ -2149,20 +2240,20 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char *line, colnr_T len, boo // flush the old data block // set ml_locked_lineadd to 0, because the updating of the // pointer blocks is done below - lineadd = buf->b_ml.ml_locked_lineadd; + int lineadd = buf->b_ml.ml_locked_lineadd; buf->b_ml.ml_locked_lineadd = 0; - (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); // flush data block + (void)ml_find_line(buf, 0, ML_FLUSH); // flush data block // update pointer blocks for the new data block for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0; stack_idx--) { infoptr_T *ip = &(buf->b_ml.ml_stack[stack_idx]); - pb_idx = ip->ip_index; + int pb_idx = ip->ip_index; if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL) { return FAIL; } - PTR_BL *pp = hp->bh_data; // must be pointer block + PointerBlock *pp = hp->bh_data; // must be pointer block if (pp->pb_id != PTR_ID) { - iemsg(_("E317: pointer block id wrong 3")); + iemsg(_(e_pointer_block_id_wrong_three)); mf_put(mfp, hp, false, false); return FAIL; } @@ -2173,7 +2264,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char *line, colnr_T len, boo if (pb_idx + 1 < (int)pp->pb_count) { memmove(&pp->pb_pointer[pb_idx + 2], &pp->pb_pointer[pb_idx + 1], - (size_t)(pp->pb_count - pb_idx - 1) * sizeof(PTR_EN)); + (size_t)(pp->pb_count - pb_idx - 1) * sizeof(PointerEntry)); } pp->pb_count++; pp->pb_pointer[pb_idx].pe_line_count = line_count_left; @@ -2212,7 +2303,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char *line, colnr_T len, boo // allocate a new pointer block // move some of the pointer into the new block // prepare for updating the parent block - for (;;) { // do this twice when splitting block 1 + while (true) { // do this twice when splitting block 1 hp_new = ml_new_ptr(mfp); if (hp_new == NULL) { // TODO(vim): try to fix tree return FAIL; @@ -2246,7 +2337,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char *line, colnr_T len, boo if (total_moved) { memmove(&pp_new->pb_pointer[0], &pp->pb_pointer[pb_idx + 1], - (size_t)(total_moved) * sizeof(PTR_EN)); + (size_t)(total_moved) * sizeof(PointerEntry)); pp_new->pb_count = (uint16_t)total_moved; pp->pb_count = (uint16_t)(pp->pb_count - (total_moved - 1)); pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right; @@ -2297,7 +2388,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char *line, colnr_T len, boo } // The line was inserted below 'lnum' - ml_updatechunk(buf, lnum + 1, (long)len, ML_CHNK_ADDLINE); + ml_updatechunk(buf, lnum + 1, len, ML_CHNK_ADDLINE); return OK; } @@ -2315,13 +2406,13 @@ void ml_add_deleted_len_buf(buf_T *buf, char *ptr, ssize_t len) if (len == -1 || len > maxlen) { len = maxlen; } - curbuf->deleted_bytes += (size_t)len + 1; - curbuf->deleted_bytes2 += (size_t)len + 1; - if (curbuf->update_need_codepoints) { - mb_utflen(ptr, (size_t)len, &curbuf->deleted_codepoints, - &curbuf->deleted_codeunits); - curbuf->deleted_codepoints++; // NL char - curbuf->deleted_codeunits++; + buf->deleted_bytes += (size_t)len + 1; + buf->deleted_bytes2 += (size_t)len + 1; + if (buf->update_need_codepoints) { + mb_utflen(ptr, (size_t)len, &buf->deleted_codepoints, + &buf->deleted_codeunits); + buf->deleted_codepoints++; // NL char + buf->deleted_codeunits++; } } @@ -2353,22 +2444,21 @@ int ml_replace_buf(buf_T *buf, linenr_T lnum, char *line, bool copy) return FAIL; } - bool readlen = true; - if (copy) { line = xstrdup(line); } - if (buf->b_ml.ml_line_lnum != lnum) { // other line buffered - ml_flush_line(buf); // flush it - } else if (buf->b_ml.ml_flags & ML_LINE_DIRTY) { // same line allocated - ml_add_deleted_len_buf(buf, buf->b_ml.ml_line_ptr, -1); - readlen = false; // already added the length - xfree(buf->b_ml.ml_line_ptr); // free it + if (buf->b_ml.ml_line_lnum != lnum) { + // another line is buffered, flush it + ml_flush_line(buf); + } + + if (kv_size(buf->update_callbacks)) { + ml_add_deleted_len_buf(buf, ml_get_buf(buf, lnum), -1); } - if (readlen && kv_size(buf->update_callbacks)) { - ml_add_deleted_len_buf(buf, ml_get_buf(buf, lnum, false), -1); + if (buf->b_ml.ml_flags & (ML_LINE_DIRTY | ML_ALLOCATED)) { + xfree(buf->b_ml.ml_line_ptr); // free allocated line } buf->b_ml.ml_line_ptr = line; @@ -2392,6 +2482,19 @@ int ml_delete(linenr_T lnum, bool message) return ml_delete_int(curbuf, lnum, message); } +/// Delete line `lnum` in buffer +/// +/// @note The caller of this function should probably also call changed_lines() after this. +/// +/// @param message Show "--No lines in buffer--" message. +/// +/// @return FAIL for failure, OK otherwise +int ml_delete_buf(buf_T *buf, linenr_T lnum, bool message) +{ + ml_flush_line(buf); + return ml_delete_int(buf, lnum, message); +} + static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message) { if (lnum < 1 || lnum > buf->b_ml.ml_line_count) { @@ -2408,7 +2511,7 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message) set_keep_msg(_(no_lines_msg), 0); } - int i = ml_replace((linenr_T)1, "", true); + int i = ml_replace_buf(buf, 1, "", true); buf->b_ml.ml_flags |= ML_EMPTY; return i; @@ -2427,7 +2530,7 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message) return FAIL; } - DATA_BL *dp = hp->bh_data; + DataBlock *dp = hp->bh_data; // compute line count (number of entries in block) before the delete int count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 2; int idx = lnum - buf->b_ml.ml_locked_low; @@ -2435,11 +2538,11 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message) buf->b_ml.ml_line_count--; int line_start = ((dp->db_index[idx]) & DB_INDEX_MASK); - long line_size; + int line_size; if (idx == 0) { // first line in block, text at the end - line_size = dp->db_txt_end - (unsigned)line_start; + line_size = (int)(dp->db_txt_end - (unsigned)line_start); } else { - line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK) - (unsigned)line_start; + line_size = (int)(((dp->db_index[idx - 1]) & DB_INDEX_MASK) - (unsigned)line_start); } // Line should always have an NL char internally (represented as NUL), @@ -2464,9 +2567,9 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message) if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL) { return FAIL; } - PTR_BL *pp = hp->bh_data; // must be pointer block + PointerBlock *pp = hp->bh_data; // must be pointer block if (pp->pb_id != PTR_ID) { - iemsg(_("E317: pointer block id wrong 4")); + iemsg(_(e_pointer_block_id_wrong_four)); mf_put(mfp, hp, false, false); return FAIL; } @@ -2476,7 +2579,7 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message) } else { if (count != idx) { // move entries after the deleted one memmove(&pp->pb_pointer[idx], &pp->pb_pointer[idx + 1], - (size_t)(count - idx) * sizeof(PTR_EN)); + (size_t)(count - idx) * sizeof(PointerEntry)); } mf_put(mfp, hp, true, false); @@ -2536,7 +2639,7 @@ void ml_setmarked(linenr_T lnum) if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL) { return; // give error message? } - DATA_BL *dp = hp->bh_data; + DataBlock *dp = hp->bh_data; dp->db_index[lnum - curbuf->b_ml.ml_locked_low] |= DB_MARKED; curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY; } @@ -2545,7 +2648,7 @@ void ml_setmarked(linenr_T lnum) linenr_T ml_firstmarked(void) { if (curbuf->b_ml.ml_mfp == NULL) { - return (linenr_T)0; + return 0; } // The search starts with lowest_marked line. This is the last line where @@ -2556,9 +2659,9 @@ linenr_T ml_firstmarked(void) // block This also releases any locked block. bhdr_T *hp; if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL) { - return (linenr_T)0; // give error message? + return 0; // give error message? } - DATA_BL *dp = hp->bh_data; + DataBlock *dp = hp->bh_data; for (int i = lnum - curbuf->b_ml.ml_locked_low; lnum <= curbuf->b_ml.ml_locked_high; i++, lnum++) { @@ -2571,7 +2674,7 @@ linenr_T ml_firstmarked(void) } } - return (linenr_T)0; + return 0; } /// clear all DB_MARKED flags @@ -2590,7 +2693,7 @@ void ml_clearmarked(void) if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL) { return; // give error message? } - DATA_BL *dp = hp->bh_data; + DataBlock *dp = hp->bh_data; for (int i = lnum - curbuf->b_ml.ml_locked_low; lnum <= curbuf->b_ml.ml_locked_high; i++, lnum++) { @@ -2639,7 +2742,7 @@ static void ml_flush_line(buf_T *buf) if (hp == NULL) { siemsg(_("E320: Cannot find line %" PRId64), (int64_t)lnum); } else { - DATA_BL *dp = hp->bh_data; + DataBlock *dp = hp->bh_data; int idx = lnum - buf->b_ml.ml_locked_low; int start = ((dp->db_index[idx]) & DB_INDEX_MASK); char *old_line = (char *)dp + start; @@ -2677,7 +2780,7 @@ static void ml_flush_line(buf_T *buf) memmove(old_line - extra, new_line, (size_t)new_len); buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS); // The else case is already covered by the insert and delete - ml_updatechunk(buf, lnum, (long)extra, ML_CHNK_UPDLINE); + ml_updatechunk(buf, lnum, extra, ML_CHNK_UPDLINE); } else { // Cannot do it in one data block: Delete and append. // Append first, because ml_delete_int() cannot delete the @@ -2693,8 +2796,11 @@ static void ml_flush_line(buf_T *buf) xfree(new_line); entered = false; + } else if (buf->b_ml.ml_flags & ML_ALLOCATED) { + xfree(buf->b_ml.ml_line_ptr); } + buf->b_ml.ml_flags &= ~(ML_LINE_DIRTY | ML_ALLOCATED); buf->b_ml.ml_line_lnum = 0; buf->b_ml.ml_line_offset = 0; } @@ -2704,7 +2810,7 @@ static bhdr_T *ml_new_data(memfile_T *mfp, bool negative, int page_count) { assert(page_count >= 0); bhdr_T *hp = mf_new(mfp, negative, (unsigned)page_count); - DATA_BL *dp = hp->bh_data; + DataBlock *dp = hp->bh_data; dp->db_id = DATA_ID; dp->db_txt_start = dp->db_txt_end = (unsigned)page_count * mfp->mf_page_size; dp->db_free = dp->db_txt_start - (unsigned)HEADER_SIZE; @@ -2717,10 +2823,10 @@ static bhdr_T *ml_new_data(memfile_T *mfp, bool negative, int page_count) static bhdr_T *ml_new_ptr(memfile_T *mfp) { bhdr_T *hp = mf_new(mfp, false, 1); - PTR_BL *pp = hp->bh_data; + PointerBlock *pp = hp->bh_data; pp->pb_id = PTR_ID; pp->pb_count = 0; - pp->pb_count_max = (uint16_t)((mfp->mf_page_size - sizeof(PTR_BL)) / sizeof(PTR_EN) + 1); + pp->pb_count_max = PB_COUNT_MAX(mfp); return hp; } @@ -2740,7 +2846,6 @@ static bhdr_T *ml_new_ptr(memfile_T *mfp) /// @return NULL for failure, pointer to block header otherwise static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action) { - PTR_BL *pp; bhdr_T *hp; int top; @@ -2805,7 +2910,7 @@ static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action) buf->b_ml.ml_stack_top = 0; // start at the root } // search downwards in the tree until a data block is found - for (;;) { + while (true) { if ((hp = mf_get(mfp, bnum, (unsigned)page_count)) == NULL) { goto error_noblock; } @@ -2817,7 +2922,7 @@ static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action) high--; } - DATA_BL *dp = hp->bh_data; + DataBlock *dp = hp->bh_data; if (dp->db_id == DATA_ID) { // data block buf->b_ml.ml_locked = hp; buf->b_ml.ml_locked_low = low; @@ -2827,9 +2932,9 @@ static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action) return hp; } - pp = (PTR_BL *)(dp); // must be pointer block + PointerBlock *pp = (PointerBlock *)(dp); // must be pointer block if (pp->pb_id != PTR_ID) { - iemsg(_("E317: pointer block id wrong")); + iemsg(_(e_pointer_block_id_wrong)); goto error_block; } @@ -2867,10 +2972,10 @@ static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action) } if (idx >= (int)pp->pb_count) { // past the end: something wrong! if (lnum > buf->b_ml.ml_line_count) { - siemsg(_("E322: line number out of range: %" PRId64 " past the end"), + siemsg(_(e_line_number_out_of_range_nr_past_the_end), (int64_t)lnum - buf->b_ml.ml_line_count); } else { - siemsg(_("E323: line count wrong in block %" PRId64), bnum); + siemsg(_(e_line_count_wrong_in_block_nr), bnum); } goto error_block; } @@ -2937,10 +3042,10 @@ static void ml_lineadd(buf_T *buf, int count) if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL) { break; } - PTR_BL *pp = hp->bh_data; // must be pointer block + PointerBlock *pp = hp->bh_data; // must be pointer block if (pp->pb_id != PTR_ID) { mf_put(mfp, hp, false, false); - iemsg(_("E317: pointer block id wrong 2")); + iemsg(_(e_pointer_block_id_wrong_two)); break; } pp->pb_pointer[ip->ip_index].pe_line_count += count; @@ -2969,7 +3074,7 @@ int resolve_symlink(const char *fname, char *buf) // Put the result so far in tmp[], starting with the original name. xstrlcpy(tmp, fname, MAXPATHL); - for (;;) { + while (true) { // Limit symlink depth to 100, catch recursive loops. if (++depth == 100) { semsg(_("E773: Symlink loop for \"%s\""), fname); @@ -3017,7 +3122,7 @@ int resolve_symlink(const char *fname, char *buf) } #endif -/// Make swap file name out of the file name and a directory name. +/// Make swapfile name out of the file name and a directory name. /// /// @return pointer to allocated memory or NULL. char *makeswapname(char *fname, char *ffname, buf_T *buf, char *dir_name) @@ -3026,9 +3131,9 @@ char *makeswapname(char *fname, char *ffname, buf_T *buf, char *dir_name) #ifdef HAVE_READLINK char fname_buf[MAXPATHL]; - // Expand symlink in the file name, so that we put the swap file with the + // Expand symlink in the file name, so that we put the swapfile with the // actual file instead of with the symlink. - if (resolve_symlink(fname, (char *)fname_buf) == OK) { + if (resolve_symlink(fname, fname_buf) == OK) { fname_res = fname_buf; } #endif @@ -3047,7 +3152,7 @@ char *makeswapname(char *fname, char *ffname, buf_T *buf, char *dir_name) return r; } - // Prepend a '.' to the swap file name for the current directory. + // Prepend a '.' to the swapfile name for the current directory. char *r = modname(fname_res, ".swp", dir_name[0] == '.' && dir_name[1] == NUL); if (r == NULL) { // out of memory @@ -3059,14 +3164,11 @@ char *makeswapname(char *fname, char *ffname, buf_T *buf, char *dir_name) return s; } -/// Get file name to use for swap file or backup file. -/// Use the name of the edited file "fname" and an entry in the 'dir' or 'bdir' -/// option "dname". -/// - If "dname" is ".", return "fname" (swap file in dir of file). -/// - If "dname" starts with "./", insert "dname" in "fname" (swap file -/// relative to dir of file). -/// - Otherwise, prepend "dname" to the tail of "fname" (swap file in specific -/// dir). +/// Get file name to use for swapfile or backup file. +/// Use the name of the edited file "fname" and an entry in the 'dir' or 'bdir' option "dname". +/// - If "dname" is ".", return "fname" (swapfile in dir of file). +/// - If "dname" starts with "./", insert "dname" in "fname" (swapfile relative to dir of file). +/// - Otherwise, prepend "dname" to the tail of "fname" (swapfile in specific dir). /// /// The return value is an allocated string and can be NULL. /// @@ -3097,10 +3199,10 @@ char *get_file_in_dir(char *fname, char *dname) return retval; } -/// Print the ATTENTION message: info about an existing swap file. +/// Print the ATTENTION message: info about an existing swapfile. /// /// @param buf buffer being edited -/// @param fname swap file name +/// @param fname swapfile name static void attention_message(buf_T *buf, char *fname) { assert(buf->b_fname != NULL); @@ -3112,7 +3214,7 @@ static void attention_message(buf_T *buf, char *fname) msg_puts("\"\n"); const time_t swap_mtime = swapfile_info(fname); msg_puts(_("While opening file \"")); - msg_outtrans(buf->b_fname); + msg_outtrans(buf->b_fname, 0); msg_puts("\"\n"); FileInfo file_info; if (!os_fileinfo(buf->b_fname, &file_info)) { @@ -3134,10 +3236,10 @@ static void attention_message(buf_T *buf, char *fname) " Quit, or continue with caution.\n")); msg_puts(_("(2) An edit session for this file crashed.\n")); msg_puts(_(" If this is the case, use \":recover\" or \"vim -r ")); - msg_outtrans(buf->b_fname); + msg_outtrans(buf->b_fname, 0); msg_puts(_("\"\n to recover the changes (see \":help recovery\").\n")); msg_puts(_(" If you did this already, delete the swap file \"")); - msg_outtrans(fname); + msg_outtrans(fname, 0); msg_puts(_("\"\n to avoid this message.\n")); cmdline_row = msg_row; no_wait_return--; @@ -3145,15 +3247,8 @@ static void attention_message(buf_T *buf, char *fname) /// Trigger the SwapExists autocommands. /// -/// @return a value for equivalent to do_dialog() (see below): -/// 0: still need to ask for a choice -/// 1: open read-only -/// 2: edit anyway -/// 3: recover -/// 4: delete it -/// 5: quit -/// 6: abort -static int do_swapexists(buf_T *buf, char *fname) +/// @return a value for equivalent to do_dialog(). +static sea_choice_T do_swapexists(buf_T *buf, char *fname) { set_vim_var_string(VV_SWAPNAME, fname, -1); set_vim_var_string(VV_SWAPCHOICE, NULL, -1); @@ -3168,23 +3263,23 @@ static int do_swapexists(buf_T *buf, char *fname) switch (*get_vim_var_str(VV_SWAPCHOICE)) { case 'o': - return 1; + return SEA_CHOICE_READONLY; case 'e': - return 2; + return SEA_CHOICE_EDIT; case 'r': - return 3; + return SEA_CHOICE_RECOVER; case 'd': - return 4; + return SEA_CHOICE_DELETE; case 'q': - return 5; + return SEA_CHOICE_QUIT; case 'a': - return 6; + return SEA_CHOICE_ABORT; } - return 0; + return SEA_CHOICE_NONE; } -/// Find out what name to use for the swap file for buffer 'buf'. +/// Find out what name to use for the swapfile for buffer 'buf'. /// /// Several names are tried to find one that does not exist. Last directory in /// option is automatically created. @@ -3193,24 +3288,23 @@ static int do_swapexists(buf_T *buf, char *fname) /// not being able to open the swap or undo file. /// @note May trigger SwapExists autocmd, pointers may change! /// -/// @param[in] buf Buffer for which swap file names needs to be found. +/// @param[in] buf Buffer for which swapfile names needs to be found. /// @param[in,out] dirp Pointer to a list of directories. When out of memory, /// is set to NULL. Is advanced to the next directory in /// the list otherwise. -/// @param[in] old_fname Allowed existing swap file name. Except for this +/// @param[in] old_fname Allowed existing swapfile name. Except for this /// case, name of the non-existing file is used. /// @param[in,out] found_existing_dir If points to true, then new directory -/// for swap file is not created. At first +/// for swapfile is not created. At first /// findswapname() call this argument must /// point to false. This parameter may only /// be set to true by this function, it is /// never set to false. /// -/// @return [allocated] Name of the swap file. +/// @return [allocated] Name of the swapfile. static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_existing_dir) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1, 2, 4) { - size_t n; char *buf_fname = buf->b_fname; // Isolate a directory name from *dirp and put it in dir_name. @@ -3219,10 +3313,11 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_ char *dir_name = xmalloc(dir_len); (void)copy_option_part(dirp, dir_name, dir_len, ","); - // we try different names until we find one that does not exist yet + // We try different swapfile names until we find one that does not exist yet. char *fname = makeswapname(buf_fname, buf->b_ffname, buf, dir_name); - for (;;) { + while (true) { + size_t n; if (fname == NULL) { // must be out of memory break; } @@ -3231,7 +3326,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_ break; } // check if the swapfile already exists - // Extra security check: When a swap file is a symbolic link, this + // Extra security check: When a swapfile is a symbolic link, this // is most likely a symlink attack. FileInfo file_info; bool file_or_link_found = os_fileinfo_link(fname, &file_info); @@ -3250,37 +3345,36 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_ // Give an error message, unless recovering, no file name, we are // viewing a help file or when the path of the file is different // (happens when all .swp files are in one directory). - if (!recoverymode && buf_fname != NULL - && !buf->b_help && !(buf->b_flags & BF_DUMMY)) { + if (!recoverymode && buf_fname != NULL && !buf->b_help && !(buf->b_flags & BF_DUMMY)) { int fd; - struct block0 b0; + ZeroBlock b0; int differ = false; - // Try to read block 0 from the swap file to get the original - // file name (and inode number). + // Try to read block 0 from the swapfile to get the original file name (and inode number). fd = os_open(fname, O_RDONLY, 0); if (fd >= 0) { if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0)) { + process_running = swapfile_process_running(&b0, fname); + // If the swapfile has the same directory as the // buffer don't compare the directory names, they can // have a different mountpoint. if (b0.b0_flags & B0_SAME_DIR) { if (path_fnamecmp(path_tail(buf->b_ffname), - path_tail((char *)b0.b0_fname)) != 0 + path_tail(b0.b0_fname)) != 0 || !same_directory(fname, buf->b_ffname)) { // Symlinks may point to the same file even // when the name differs, need to check the // inode too. - expand_env((char *)b0.b0_fname, NameBuff, MAXPATHL); + expand_env(b0.b0_fname, NameBuff, MAXPATHL); if (fnamecmp_ino(buf->b_ffname, NameBuff, char_to_long(b0.b0_ino))) { differ = true; } } } else { - // The name in the swap file may be - // "~user/path/file". Expand it first. - expand_env((char *)b0.b0_fname, NameBuff, MAXPATHL); + // The name in the swapfile may be "~user/path/file". Expand it first. + expand_env(b0.b0_fname, NameBuff, MAXPATHL); if (fnamecmp_ino(buf->b_ffname, NameBuff, char_to_long(b0.b0_ino))) { differ = true; @@ -3290,18 +3384,18 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_ close(fd); } - // give the ATTENTION message when there is an old swap file - // for the current file, and the buffer was not recovered. + // Show the ATTENTION message when: + // - there is an old swapfile for the current file + // - the buffer was not recovered if (differ == false && !(curbuf->b_flags & BF_RECOVERED) && vim_strchr(p_shm, SHM_ATTENTION) == NULL) { - int choice = 0; + sea_choice_T choice = SEA_CHOICE_NONE; - process_still_running = false; - // It's safe to delete the swap file if all these are true: + // It's safe to delete the swapfile if all these are true: // - the edited file exists - // - the swap file has no changes and looks OK + // - the swapfile has no changes and looks OK if (os_path_exists(buf->b_fname) && swapfile_unchanged(fname)) { - choice = 4; + choice = SEA_CHOICE_DELETE; if (p_verbose > 0) { verb_msg(_("Found a swap file that is not useful, deleting it")); } @@ -3309,14 +3403,20 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_ // If there is a SwapExists autocommand and we can handle the // response, trigger it. It may return 0 to ask the user anyway. - if (choice == 0 + if (choice == SEA_CHOICE_NONE && swap_exists_action != SEA_NONE && has_autocmd(EVENT_SWAPEXISTS, buf_fname, buf)) { choice = do_swapexists(buf, fname); } - if (choice == 0) { - // Show info about the existing swap file. + if (choice == SEA_CHOICE_NONE && swap_exists_action == SEA_READONLY) { + // always open readonly. + choice = SEA_CHOICE_READONLY; + } + + process_running = 0; // Set by attention_message..swapfile_info. + if (choice == SEA_CHOICE_NONE) { + // Show info about the existing swapfile. attention_message(buf, fname); // We don't want a 'q' typed at the more-prompt @@ -3328,7 +3428,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_ flush_buffers(FLUSH_TYPEAHEAD); } - if (swap_exists_action != SEA_NONE && choice == 0) { + if (swap_exists_action != SEA_NONE && choice == SEA_CHOICE_NONE) { const char *const sw_msg_1 = _("Swap file \""); const char *const sw_msg_2 = _("\" already exists!"); @@ -3342,65 +3442,66 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_ memcpy(name, sw_msg_1, sw_msg_1_len + 1); home_replace(NULL, fname, &name[sw_msg_1_len], fname_len, true); xstrlcat(name, sw_msg_2, name_len); - choice = do_dialog(VIM_WARNING, _("VIM - ATTENTION"), - name, - process_still_running - ? _("&Open Read-Only\n&Edit anyway\n&Recover" - "\n&Quit\n&Abort") : - _("&Open Read-Only\n&Edit anyway\n&Recover" - "\n&Delete it\n&Quit\n&Abort"), - 1, NULL, false); - - if (process_still_running && choice >= 4) { - choice++; // Skip missing "Delete it" button. + int dialog_result + = do_dialog(VIM_WARNING, + _("VIM - ATTENTION"), + name, + process_running + ? _("&Open Read-Only\n&Edit anyway\n&Recover\n&Quit\n&Abort") + : _("&Open Read-Only\n&Edit anyway\n&Recover\n&Delete it\n&Quit\n&Abort"), + 1, NULL, false); + + if (process_running && dialog_result >= 4) { + // compensate for missing "Delete it" button + dialog_result++; } + choice = (sea_choice_T)dialog_result; xfree(name); // pretend screen didn't scroll, need redraw anyway msg_reset_scroll(); } - if (choice > 0) { - switch (choice) { - case 1: - buf->b_p_ro = true; - break; - case 2: - break; - case 3: - swap_exists_action = SEA_RECOVER; - break; - case 4: - os_remove(fname); - break; - case 5: - swap_exists_action = SEA_QUIT; - break; - case 6: - swap_exists_action = SEA_QUIT; - got_int = true; - break; - } - - // If the file was deleted this fname can be used. - if (!os_path_exists(fname)) { - break; - } - } else { + switch (choice) { + case SEA_CHOICE_READONLY: // "Open Read-Only" + buf->b_p_ro = true; + break; + case SEA_CHOICE_EDIT: // "Edit anyway" + break; + case SEA_CHOICE_RECOVER: // "Recover" + swap_exists_action = SEA_RECOVER; + break; + case SEA_CHOICE_DELETE: // "Delete it" + os_remove(fname); + break; + case SEA_CHOICE_QUIT: // "Quit" + swap_exists_action = SEA_QUIT; + break; + case SEA_CHOICE_ABORT: // "Abort" + swap_exists_action = SEA_QUIT; + got_int = true; + break; + case SEA_CHOICE_NONE: msg_puts("\n"); if (msg_silent == 0) { // call wait_return() later need_wait_return = true; } + break; + } + + // If the swapfile was deleted this `fname` can be used. + if (choice != SEA_CHOICE_NONE && !os_path_exists(fname)) { + break; } } } } - // Change the ".swp" extension to find another file that can be used. + // Permute the ".swp" extension to find a unique swapfile name. // First decrement the last char: ".swo", ".swn", etc. // If that still isn't enough decrement the last but one char: ".svz" - // Can happen when editing many "No Name" buffers. + // Can happen when many Nvim instances are editing the same file (including "No Name" buffers). if (fname[n - 1] == 'a') { // ".s?a" if (fname[n - 2] == 'a') { // ".saa": tried enough, give up emsg(_("E326: Too many swap files found")); @@ -3418,7 +3519,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_ } else if (!*found_existing_dir && **dirp == NUL) { int ret; char *failed_dir; - if ((ret = os_mkdir_recurse(dir_name, 0755, &failed_dir)) != 0) { + if ((ret = os_mkdir_recurse(dir_name, 0755, &failed_dir, NULL)) != 0) { semsg(_("E303: Unable to create directory \"%s\" for swap file, " "recovery impossible: %s"), failed_dir, os_strerror(ret)); @@ -3430,7 +3531,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_ return fname; } -static int b0_magic_wrong(ZERO_BL *b0p) +static int b0_magic_wrong(ZeroBlock *b0p) { return b0p->b0_magic_long != B0_MAGIC_LONG || b0p->b0_magic_int != B0_MAGIC_INT @@ -3438,7 +3539,7 @@ static int b0_magic_wrong(ZERO_BL *b0p) || b0p->b0_magic_char != B0_MAGIC_CHAR; } -/// Compare current file name with file name from swap file. +/// Compare current file name with file name from swapfile. /// Try to use inode numbers when possible. /// Return non-zero when files are different. /// @@ -3448,7 +3549,7 @@ static int b0_magic_wrong(ZERO_BL *b0p) /// because the device number cannot be used over a network. /// - When a file does not exist yet (editing a new file) there is no inode /// number. -/// - The file name in a swap file may not be valid on the current host. The +/// - The file name in a swapfile may not be valid on the current host. The /// "~user" form is used whenever possible to avoid this. /// /// This is getting complicated, let's make a table: @@ -3462,7 +3563,7 @@ static int b0_magic_wrong(ZERO_BL *b0p) /// == 0 X OK OK fname_c != fname_s /// X == 0 OK OK fname_c != fname_s /// -/// current file doesn't exist, file for swap file exist, file name(s) not +/// current file doesn't exist, file for swapfile exist, file name(s) not /// available -> probably different /// == 0 != 0 FAIL X true /// == 0 != 0 X FAIL true @@ -3485,11 +3586,11 @@ static int b0_magic_wrong(ZERO_BL *b0p) /// without making the block 0 incompatible with 32 bit versions. /// /// @param fname_c current file name -/// @param fname_s file name from swap file +/// @param fname_s file name from swapfile static bool fnamecmp_ino(char *fname_c, char *fname_s, long ino_block0) { uint64_t ino_c = 0; // ino of current file - uint64_t ino_s; // ino of file from swap file + uint64_t ino_s; // ino of file from swapfile char buf_c[MAXPATHL]; // full path of fname_c char buf_s[MAXPATHL]; // full path of fname_s int retval_c; // flag: buf_c valid @@ -3501,7 +3602,7 @@ static bool fnamecmp_ino(char *fname_c, char *fname_s, long ino_block0) } // First we try to get the inode from the file name, because the inode in - // the swap file may be outdated. If that fails (e.g. this path is not + // the swapfile may be outdated. If that fails (e.g. this path is not // valid on this machine), use the inode from block 0. if (os_fileinfo(fname_s, &file_info)) { ino_s = os_fileinfo_inode(&file_info); @@ -3515,15 +3616,15 @@ static bool fnamecmp_ino(char *fname_c, char *fname_s, long ino_block0) // One of the inode numbers is unknown, try a forced vim_FullName() and // compare the file names. - retval_c = vim_FullName(fname_c, (char *)buf_c, MAXPATHL, true); - retval_s = vim_FullName(fname_s, (char *)buf_s, MAXPATHL, true); + retval_c = vim_FullName(fname_c, buf_c, MAXPATHL, true); + retval_s = vim_FullName(fname_s, buf_s, MAXPATHL, true); if (retval_c == OK && retval_s == OK) { return strcmp(buf_c, buf_s) != 0; } // Can't compare inodes or file names, guess that the files are different, // unless both appear not to exist at all, then compare with the file name - // in the swap file. + // in the swapfile. if (ino_s == 0 && ino_c == 0 && retval_c == FAIL && retval_s == FAIL) { return strcmp(fname_c, fname_s) != 0; } @@ -3532,22 +3633,23 @@ static bool fnamecmp_ino(char *fname_c, char *fname_s, long ino_block0) /// Move a long integer into a four byte character array. /// Used for machine independency in block zero. -static void long_to_char(long n, char_u *s) +static void long_to_char(long n, char *s_in) { - s[0] = (char_u)(n & 0xff); + uint8_t *s = (uint8_t *)s_in; + s[0] = (uint8_t)(n & 0xff); n = (unsigned)n >> 8; - s[1] = (char_u)(n & 0xff); + s[1] = (uint8_t)(n & 0xff); n = (unsigned)n >> 8; - s[2] = (char_u)(n & 0xff); + s[2] = (uint8_t)(n & 0xff); n = (unsigned)n >> 8; - s[3] = (char_u)(n & 0xff); + s[3] = (uint8_t)(n & 0xff); } -static long char_to_long(const char_u *s) +static long char_to_long(const char *s_in) { - long retval; + const uint8_t *s = (uint8_t *)s_in; - retval = s[3]; + long retval = s[3]; retval <<= 8; retval |= s[2]; retval <<= 8; @@ -3558,28 +3660,23 @@ static long char_to_long(const char_u *s) return retval; } -/// Set the flags in the first block of the swap file: +/// Set the flags in the first block of the swapfile: /// - file is modified or not: buf->b_changed /// - 'fileformat' /// - 'fileencoding' void ml_setflags(buf_T *buf) { - bhdr_T *hp; - ZERO_BL *b0p; - if (!buf->b_ml.ml_mfp) { return; } - for (hp = buf->b_ml.ml_mfp->mf_used_last; hp != NULL; hp = hp->bh_prev) { - if (hp->bh_bnum == 0) { - b0p = hp->bh_data; - b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0; - b0p->b0_flags = (char)((b0p->b0_flags & ~B0_FF_MASK) | (uint8_t)(get_fileformat(buf) + 1)); - add_b0_fenc(b0p, buf); - hp->bh_flags |= BH_DIRTY; - mf_sync(buf->b_ml.ml_mfp, MFS_ZERO); - break; - } + bhdr_T *hp = pmap_get(int64_t)(&buf->b_ml.ml_mfp->mf_hash, 0); + if (hp) { + ZeroBlock *b0p = hp->bh_data; + b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0; + b0p->b0_flags = (char)((b0p->b0_flags & ~B0_FF_MASK) | (uint8_t)(get_fileformat(buf) + 1)); + add_b0_fenc(b0p, buf); + hp->bh_flags |= BH_DIRTY; + mf_sync(buf->b_ml.ml_mfp, MFS_ZERO); } } @@ -3595,7 +3692,7 @@ enum { /// Careful: ML_CHNK_ADDLINE may cause ml_find_line() to be called. /// ML_CHNK_DELLINE: Subtract len from parent chunk, possibly deleting it /// ML_CHNK_UPDLINE: Add len to parent chunk, as a signed entity. -static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype) +static void ml_updatechunk(buf_T *buf, linenr_T line, int len, int updtype) { static buf_T *ml_upd_lastbuf = NULL; static linenr_T ml_upd_lastline; @@ -3604,11 +3701,7 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype) linenr_T curline = ml_upd_lastcurline; int curix = ml_upd_lastcurix; - long size; - chunksize_T *curchnk; - int rest; bhdr_T *hp; - DATA_BL *dp; if (buf->b_ml.ml_usedchunks == -1 || len == 0) { return; @@ -3625,8 +3718,7 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype) // First line in empty buffer from ml_flush_line() -- reset buf->b_ml.ml_usedchunks = 1; buf->b_ml.ml_chunksize[0].mlcs_numlines = 1; - buf->b_ml.ml_chunksize[0].mlcs_totalsize = - (long)strlen(buf->b_ml.ml_line_ptr) + 1; + buf->b_ml.ml_chunksize[0].mlcs_totalsize = (int)strlen(buf->b_ml.ml_line_ptr) + 1; return; } @@ -3647,13 +3739,15 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype) curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines; curix++; } - curchnk = buf->b_ml.ml_chunksize + curix; + chunksize_T *curchnk = buf->b_ml.ml_chunksize + curix; if (updtype == ML_CHNK_DELLINE) { len = -len; } curchnk->mlcs_totalsize += len; if (updtype == ML_CHNK_ADDLINE) { + int rest; + DataBlock *dp; curchnk->mlcs_numlines++; // May resize here so we don't have to do it in both cases below @@ -3664,17 +3758,14 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype) } if (buf->b_ml.ml_chunksize[curix].mlcs_numlines >= MLCS_MAXL) { - int count; // number of entries in block - int idx; int text_end; - int linecnt; memmove(buf->b_ml.ml_chunksize + curix + 1, buf->b_ml.ml_chunksize + curix, (size_t)(buf->b_ml.ml_usedchunks - curix) * sizeof(chunksize_T)); // Compute length of first half of lines in the split chunk - size = 0; - linecnt = 0; + int size = 0; + int linecnt = 0; while (curline < buf->b_ml.ml_line_count && linecnt < MLCS_MINL) { if ((hp = ml_find_line(buf, curline, ML_FIND)) == NULL) { @@ -3682,8 +3773,9 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype) return; } dp = hp->bh_data; - count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 1; - idx = curline - buf->b_ml.ml_locked_low; + int count + = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 1; // number of entries in block + int idx = curline - buf->b_ml.ml_locked_low; curline = buf->b_ml.ml_locked_high + 1; if (idx == 0) { // first line in block, text at the end text_end = (int)dp->db_txt_end; @@ -3786,19 +3878,11 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype) /// @param no_ff ignore 'fileformat' option, always use one byte for NL. /// /// @return -1 if information is not available -long ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp, bool no_ff) +int ml_find_line_or_offset(buf_T *buf, linenr_T lnum, int *offp, bool no_ff) { - linenr_T curline; - int curix; - long size; bhdr_T *hp; - DATA_BL *dp; - int count; // number of entries in block - int idx; - int start_idx; int text_end; - long offset; - int len; + int offset; int ffdos = !no_ff && (get_fileformat(buf) == EOL_DOS); int extra = 0; @@ -3816,12 +3900,17 @@ long ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp, bool no_ff) if (lnum == 0 || buf->b_ml.ml_line_lnum < lnum || !no_ff) { ml_flush_line(curbuf); } else if (can_cache && buf->b_ml.ml_line_offset > 0) { - return (long)buf->b_ml.ml_line_offset; + return (int)buf->b_ml.ml_line_offset; } if (buf->b_ml.ml_usedchunks == -1 || buf->b_ml.ml_chunksize == NULL || lnum < 0) { + // memline is currently empty. Although if it is loaded, + // it behaves like there is one empty line. + if (!ffdos && buf->b_ml.ml_mfp && (lnum == 1 || lnum == 2)) { + return lnum - 1; + } return -1; } @@ -3835,16 +3924,16 @@ long ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp, bool no_ff) } // Find the last chunk before the one containing our line. Last chunk is // special because it will never qualify - curline = 1; - curix = 0; - size = 0; + linenr_T curline = 1; + int curix = 0; + int size = 0; while (curix < buf->b_ml.ml_usedchunks - 1 && ((lnum != 0 && lnum >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines) || (offset != 0 && offset > size + buf->b_ml.ml_chunksize[curix].mlcs_totalsize - + (long)ffdos * buf->b_ml.ml_chunksize[curix].mlcs_numlines))) { + + ffdos * buf->b_ml.ml_chunksize[curix].mlcs_numlines))) { curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines; size += buf->b_ml.ml_chunksize[curix].mlcs_totalsize; if (offset && ffdos) { @@ -3858,9 +3947,11 @@ long ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp, bool no_ff) || (hp = ml_find_line(buf, curline, ML_FIND)) == NULL) { return -1; } - dp = hp->bh_data; - count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 1; - start_idx = idx = curline - buf->b_ml.ml_locked_low; + DataBlock *dp = hp->bh_data; + int count + = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 1; // number of entries in block + int idx; + int start_idx = idx = curline - buf->b_ml.ml_locked_low; if (idx == 0) { // first line in block, text at the end text_end = (int)dp->db_txt_end; } else { @@ -3888,7 +3979,7 @@ long ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp, bool no_ff) idx++; } } - len = text_end - (int)((dp->db_index[idx]) & DB_INDEX_MASK); + int len = text_end - (int)((dp->db_index[idx]) & DB_INDEX_MASK); size += len; if (offset != 0 && size >= offset) { if (size + ffdos == offset) { @@ -3930,16 +4021,16 @@ long ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp, bool no_ff) } /// Goto byte in buffer with offset 'cnt'. -void goto_byte(long cnt) +void goto_byte(int cnt) { - long boff = cnt; + int boff = cnt; ml_flush_line(curbuf); // cached line may be dirty setpcmark(); if (boff) { boff--; } - linenr_T lnum = (linenr_T)ml_find_line_or_offset(curbuf, (linenr_T)0, &boff, false); + linenr_T lnum = (linenr_T)ml_find_line_or_offset(curbuf, 0, &boff, false); if (lnum < 1) { // past the end curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; curwin->w_curswant = MAXCOL; @@ -3968,7 +4059,7 @@ int inc(pos_T *lp) if (lp->col != MAXCOL) { const char *const p = ml_get_pos(lp); if (*p != NUL) { // still within line, move to next char (may be NUL) - const int l = utfc_ptr2len((char *)p); + const int l = utfc_ptr2len(p); lp->col += l; return ((p[l] != NUL) ? 0 : 2); |