aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/memline.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/memline.c')
-rw-r--r--src/nvim/memline.c268
1 files changed, 172 insertions, 96 deletions
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 662eda3c7c..a071314453 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -71,6 +71,7 @@
#include "nvim/undo.h"
#include "nvim/window.h"
#include "nvim/os/os.h"
+#include "nvim/os/process.h"
#include "nvim/os/input.h"
#ifndef UNIX /* it's in os/unix_defs.h for Unix */
@@ -348,7 +349,7 @@ int ml_open(buf_T *buf)
/*
* Allocate first data block and create an empty line 1.
*/
- hp = ml_new_data(mfp, FALSE, 1);
+ hp = ml_new_data(mfp, false, 1);
if (hp->bh_bnum != 2) {
IEMSG(_("E298: Didn't get block nr 2?"));
goto error;
@@ -763,7 +764,7 @@ void ml_recover(void)
long error;
int cannot_open;
linenr_T line_count;
- int has_error;
+ bool has_error;
int idx;
int top;
int txt_start;
@@ -983,8 +984,9 @@ void ml_recover(void)
* 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);
+ while (!(curbuf->b_ml.ml_flags & ML_EMPTY)) {
+ ml_delete((linenr_T)1, false);
+ }
/*
* Try reading the original file to obtain the values of 'fileformat',
@@ -1033,8 +1035,8 @@ void ml_recover(void)
}
++error;
ml_append(lnum++, (char_u *)_("???MANY LINES MISSING"),
- (colnr_T)0, TRUE);
- } else { /* there is a block */
+ (colnr_T)0, true);
+ } else { // there is a block
pp = hp->bh_data;
if (pp->pb_id == PTR_ID) { /* it is a pointer block */
/* check line count when using pointer block first time */
@@ -1044,15 +1046,15 @@ void ml_recover(void)
if (line_count != 0) {
++error;
ml_append(lnum++, (char_u *)_("???LINE COUNT WRONG"),
- (colnr_T)0, TRUE);
+ (colnr_T)0, true);
}
}
if (pp->pb_count == 0) {
ml_append(lnum++, (char_u *)_("???EMPTY BLOCK"),
- (colnr_T)0, TRUE);
- ++error;
- } else if (idx < (int)pp->pb_count) { /* go a block deeper */
+ (colnr_T)0, true);
+ error++;
+ } else if (idx < (int)pp->pb_count) { // go a block deeper
if (pp->pb_pointer[idx].pe_bnum < 0) {
/*
* Data block with negative block number.
@@ -1072,7 +1074,7 @@ void ml_recover(void)
if (cannot_open) {
++error;
ml_append(lnum++, (char_u *)_("???LINES MISSING"),
- (colnr_T)0, TRUE);
+ (colnr_T)0, true);
}
++idx; /* get same block again for next index */
continue;
@@ -1102,23 +1104,21 @@ void ml_recover(void)
}
++error;
ml_append(lnum++, (char_u *)_("???BLOCK MISSING"),
- (colnr_T)0, TRUE);
+ (colnr_T)0, true);
} else {
- /*
- * it is a data block
- * Append all the lines in this block
- */
- has_error = FALSE;
- /*
- * check length of block
- * if wrong, use length in pointer block
- */
+ // it is a data block
+ // Append all the lines in this block
+ has_error = false;
+ // check length of block
+ // if wrong, use length in pointer block
if (page_count * mfp->mf_page_size != dp->db_txt_end) {
- ml_append(lnum++,
- (char_u *)_("??? from here until ???END lines may be messed up"),
- (colnr_T)0, TRUE);
- ++error;
- has_error = TRUE;
+ ml_append(
+ lnum++,
+ (char_u *)_("??? from here until ???END lines"
+ " may be messed up"),
+ (colnr_T)0, true);
+ error++;
+ has_error = true;
dp->db_txt_end = page_count * mfp->mf_page_size;
}
@@ -1130,12 +1130,13 @@ void ml_recover(void)
* if wrong, use count in data block
*/
if (line_count != dp->db_line_count) {
- ml_append(lnum++,
- (char_u *)_(
- "??? from here until ???END lines may have been inserted/deleted"),
- (colnr_T)0, TRUE);
- ++error;
- has_error = TRUE;
+ ml_append(
+ lnum++,
+ (char_u *)_("??? from here until ???END lines"
+ " may have been inserted/deleted"),
+ (colnr_T)0, true);
+ error++;
+ has_error = true;
}
for (i = 0; i < dp->db_line_count; ++i) {
@@ -1146,11 +1147,11 @@ void ml_recover(void)
++error;
} else
p = (char_u *)dp + txt_start;
- ml_append(lnum++, p, (colnr_T)0, TRUE);
+ ml_append(lnum++, p, (colnr_T)0, true);
+ }
+ if (has_error) {
+ ml_append(lnum++, (char_u *)_("???END"), (colnr_T)0, true);
}
- if (has_error)
- ml_append(lnum++, (char_u *)_("???END"),
- (colnr_T)0, TRUE);
}
}
}
@@ -1201,7 +1202,7 @@ void ml_recover(void)
*/
while (curbuf->b_ml.ml_line_count > lnum
&& !(curbuf->b_ml.ml_flags & ML_EMPTY))
- ml_delete(curbuf->b_ml.ml_line_count, FALSE);
+ ml_delete(curbuf->b_ml.ml_line_count, false);
curbuf->b_flags |= BF_RECOVERED;
recoverymode = FALSE;
@@ -1453,14 +1454,47 @@ static char *make_percent_swname(const char *dir, char *name)
return d;
}
-#ifdef UNIX
static bool process_still_running;
-#endif
-/*
- * Give information about an existing swap file.
- * Returns timestamp (0 when unknown).
- */
+/// Return information found in swapfile "fname" in dictionary "d".
+/// This is used by the swapinfo() function.
+void get_b0_dict(const char *fname, dict_T *d)
+{
+ int fd;
+ struct block0 b0;
+
+ if ((fd = os_open(fname, O_RDONLY, 0)) >= 0) {
+ if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0)) {
+ if (ml_check_b0_id(&b0) == FAIL) {
+ tv_dict_add_str(d, S_LEN("error"), "Not a swap file");
+ } else if (b0_magic_wrong(&b0)) {
+ tv_dict_add_str(d, S_LEN("error"), "Magic number mismatch");
+ } else {
+ // We have swap information.
+ tv_dict_add_str_len(d, S_LEN("version"), (char *)b0.b0_version, 10);
+ tv_dict_add_str_len(d, S_LEN("user"), (char *)b0.b0_uname,
+ B0_UNAME_SIZE);
+ tv_dict_add_str_len(d, S_LEN("host"), (char *)b0.b0_hname,
+ B0_HNAME_SIZE);
+ tv_dict_add_str_len(d, S_LEN("fname"), (char *)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("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));
+ }
+ } else {
+ tv_dict_add_str(d, S_LEN("error"), "Cannot read file");
+ }
+ close(fd);
+ } else {
+ tv_dict_add_str(d, S_LEN("error"), "Cannot open file");
+ }
+}
+
+/// Give information about an existing swap file.
+/// Returns timestamp (0 when unknown).
static time_t swapfile_info(char_u *fname)
{
assert(fname != NULL);
@@ -1530,12 +1564,10 @@ static time_t swapfile_info(char_u *fname)
if (char_to_long(b0.b0_pid) != 0L) {
MSG_PUTS(_("\n process ID: "));
msg_outnum(char_to_long(b0.b0_pid));
-#if defined(UNIX)
- if (kill((pid_t)char_to_long(b0.b0_pid), 0) == 0) {
+ if (os_proc_running((int)char_to_long(b0.b0_pid))) {
MSG_PUTS(_(" (STILL RUNNING)"));
process_still_running = true;
}
-#endif
}
if (b0_magic_wrong(&b0)) {
@@ -1552,6 +1584,51 @@ static time_t swapfile_info(char_u *fname)
return x;
}
+/// Returns TRUE if the swap file looks OK and there are no changes, thus it
+/// can be safely deleted.
+static time_t swapfile_unchanged(char *fname)
+{
+ struct block0 b0;
+ int ret = true;
+
+ // Swap file must exist.
+ if (!os_path_exists((char_u *)fname)) {
+ return false;
+ }
+
+ // must be able to read the first block
+ int fd = os_open(fname, O_RDONLY, 0);
+ if (fd < 0) {
+ return false;
+ }
+ if (read_eintr(fd, &b0, sizeof(b0)) != sizeof(b0)) {
+ close(fd);
+ return false;
+ }
+
+ // the ID and magic number must be correct
+ if (ml_check_b0_id(&b0) == FAIL|| b0_magic_wrong(&b0)) {
+ ret = false;
+ }
+
+ // must be unchanged
+ if (b0.b0_dirty) {
+ ret = false;
+ }
+
+ // process must be known and not running.
+ long pid = char_to_long(b0.b0_pid);
+ if (pid == 0L || os_proc_running((int)pid)) {
+ ret = false;
+ }
+
+ // TODO(bram): Should we check if the swap file was created on the current
+ // system? And the current user?
+
+ close(fd);
+ return ret;
+}
+
static int recov_file_names(char_u **names, char_u *path, int prepend_dot)
FUNC_ATTR_NONNULL_ALL
{
@@ -1737,7 +1814,7 @@ char_u *
ml_get_buf (
buf_T *buf,
linenr_T lnum,
- int will_change /* line will be changed */
+ bool will_change // line will be changed
)
{
bhdr_T *hp;
@@ -1823,12 +1900,11 @@ int ml_line_alloced(void)
*
* return FAIL for failure, OK otherwise
*/
-int
-ml_append (
- linenr_T lnum, /* append after this line (can be 0) */
- char_u *line, /* text of the new line */
- colnr_T len, /* length of new line, including NUL, or 0 */
- int newfile /* flag, see above */
+int ml_append(
+ linenr_T lnum, // append after this line (can be 0)
+ char_u *line, // text of the new line
+ colnr_T len, // length of new line, including NUL, or 0
+ bool newfile // flag, see above
)
{
/* When starting up, we might still need to create the memfile */
@@ -1844,13 +1920,12 @@ ml_append (
* Like ml_append() but for an arbitrary buffer. The buffer must already have
* a memline.
*/
-int
-ml_append_buf (
+int ml_append_buf(
buf_T *buf,
- linenr_T lnum, /* append after this line (can be 0) */
- char_u *line, /* text of the new line */
- colnr_T len, /* length of new line, including NUL, or 0 */
- int newfile /* flag, see above */
+ linenr_T lnum, // append after this line (can be 0)
+ char_u *line, // text of the new line
+ colnr_T len, // length of new line, including NUL, or 0
+ bool newfile // flag, see above
)
{
if (buf->b_ml.ml_mfp == NULL)
@@ -1861,14 +1936,13 @@ ml_append_buf (
return ml_append_int(buf, lnum, line, len, newfile, FALSE);
}
-static int
-ml_append_int (
+static int ml_append_int(
buf_T *buf,
- linenr_T lnum, /* append after this line (can be 0) */
- char_u *line, /* text of the new line */
- colnr_T len, /* length of line, including NUL, or 0 */
- int newfile, /* flag, see above */
- int mark /* mark the new line */
+ linenr_T lnum, // append after this line (can be 0)
+ char_u *line, // text of the new line
+ colnr_T len, // length of line, including NUL, or 0
+ bool newfile, // flag, see above
+ int mark // mark the new line
)
{
int i;
@@ -2351,13 +2425,13 @@ int ml_replace(linenr_T lnum, char_u *line, bool copy)
///
/// @param message Show "--No lines in buffer--" message.
/// @return FAIL for failure, OK otherwise
-int ml_delete(linenr_T lnum, int message)
+int ml_delete(linenr_T lnum, bool message)
{
ml_flush_line(curbuf);
return ml_delete_int(curbuf, lnum, message);
}
-static int ml_delete_int(buf_T *buf, linenr_T lnum, int message)
+static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message)
{
bhdr_T *hp;
memfile_T *mfp;
@@ -2677,17 +2751,15 @@ static void ml_flush_line(buf_T *buf)
/* The else case is already covered by the insert and delete */
ml_updatechunk(buf, lnum, (long)extra, ML_CHNK_UPDLINE);
} else {
- /*
- * Cannot do it in one data block: Delete and append.
- * Append first, because ml_delete_int() cannot delete the
- * last line in a buffer, which causes trouble for a buffer
- * that has only one line.
- * Don't forget to copy the mark!
- */
- /* How about handling errors??? */
- (void)ml_append_int(buf, lnum, new_line, new_len, FALSE,
- (dp->db_index[idx] & DB_MARKED));
- (void)ml_delete_int(buf, lnum, FALSE);
+ // Cannot do it in one data block: Delete and append.
+ // Append first, because ml_delete_int() cannot delete the
+ // last line in a buffer, which causes trouble for a buffer
+ // that has only one line.
+ // Don't forget to copy the mark!
+ // How about handling errors???
+ (void)ml_append_int(buf, lnum, new_line, new_len, false,
+ (dp->db_index[idx] & DB_MARKED));
+ (void)ml_delete_int(buf, lnum, false);
}
}
xfree(new_line);
@@ -2701,7 +2773,7 @@ static void ml_flush_line(buf_T *buf)
/*
* create a new, empty, data block
*/
-static bhdr_T *ml_new_data(memfile_T *mfp, int negative, int page_count)
+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);
@@ -3353,17 +3425,24 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname,
&& vim_strchr(p_shm, SHM_ATTENTION) == NULL) {
int choice = 0;
-#ifdef UNIX
process_still_running = false;
-#endif
- /*
- * If there is a SwapExists autocommand and we can handle
- * the response, trigger it. It may return 0 to ask the
- * user anyway.
- */
- if (swap_exists_action != SEA_NONE
- && has_autocmd(EVENT_SWAPEXISTS, (char_u *) buf_fname, buf))
- choice = do_swapexists(buf, (char_u *) fname);
+ // It's safe to delete the swap file if all these are true:
+ // - the edited file exists
+ // - the swap file has no changes and looks OK
+ if (os_path_exists(buf->b_fname) && swapfile_unchanged(fname)) {
+ choice = 4;
+ if (p_verbose > 0) {
+ verb_msg(_("Found a swap file that is not useful, deleting it"));
+ }
+ }
+
+ // 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
+ && swap_exists_action != SEA_NONE
+ && has_autocmd(EVENT_SWAPEXISTS, (char_u *)buf_fname, buf)) {
+ choice = do_swapexists(buf, (char_u *)fname);
+ }
if (choice == 0) {
// Show info about the existing swap file.
@@ -3395,21 +3474,18 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname,
xstrlcat(name, sw_msg_2, name_len);
choice = do_dialog(VIM_WARNING, (char_u *)_("VIM - ATTENTION"),
(char_u *)name,
-# if defined(UNIX)
process_still_running
? (char_u *)_(
"&Open Read-Only\n&Edit anyway\n&Recover"
"\n&Quit\n&Abort") :
-# endif
(char_u *)_(
"&Open Read-Only\n&Edit anyway\n&Recover"
"\n&Delete it\n&Quit\n&Abort"),
1, NULL, false);
-# if defined(UNIX)
- if (process_still_running && choice >= 4)
- choice++; /* Skip missing "Delete it" button */
-# endif
+ if (process_still_running && choice >= 4) {
+ choice++; // Skip missing "Delete it" button.
+ }
xfree(name);
// pretend screen didn't scroll, need redraw anyway