From 24b7462b3cbf9a70f07f0a8e8da562a365cdc37a Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 29 Apr 2019 19:20:22 +0200 Subject: vim-patch:8.1.0313: information about a swap file is unavailable Problem: Information about a swap file is unavailable. Solution: Add swapinfo(). (Enzo Ferber) https://github.com/vim/vim/commit/00f123a56585363cd13f062fd3bb123efcfaa664 --- runtime/doc/eval.txt | 17 +++++++++++++++++ src/nvim/eval.c | 7 +++++++ src/nvim/eval.lua | 1 + src/nvim/memline.c | 39 +++++++++++++++++++++++++++++++++++---- src/nvim/testdir/test_swap.vim | 34 ++++++++++++++++++++++++++++++++++ 5 files changed, 94 insertions(+), 4 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index b425e1b591..0472a67d18 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2321,6 +2321,7 @@ submatch({nr} [, {list}]) String or List specific match in ":s" or substitute() substitute({expr}, {pat}, {sub}, {flags}) String all {pat} in {expr} replaced with {sub} +swapinfo({fname}) Dict information about swap file {fname} synID({lnum}, {col}, {trans}) Number syntax ID at {lnum} and {col} synIDattr({synID}, {what} [, {mode}]) String attribute {what} of syntax ID {synID} @@ -7758,6 +7759,22 @@ substitute({expr}, {pat}, {sub}, {flags}) *substitute()* |submatch()| returns. Example: > :echo substitute(s, '\(\x\x\)', {m -> '0x' . m[1]}, 'g') +swapinfo({fname}) swapinfo() + The result is a dictionary, which holds information about the + swapfile {fname}. The available fields are: + version VIM version + user user name + host host name + fname original file name + pid PID of the VIM process that created the swap + file + mtime last modification time in seconds + inode Optional: INODE number of the file + In case of failure an "error" item is added with the reason: + Cannot open file: file not found or in accessible + Cannot read file: cannot read first block + magic number mismatch: info in first block is invalid + synID({lnum}, {col}, {trans}) *synID()* The result is a Number, which is the syntax ID at the position {lnum} and {col} in the current window. diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 19324acd5c..e9da1f2f2b 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -16423,6 +16423,13 @@ static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } +/// "swapinfo(swap_filename)" function +static void f_swapinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + tv_dict_alloc_ret(rettv); + get_b0_dict(tv_get_string(argvars), rettv->vval.v_dict); +} + /// "synID(lnum, col, trans)" function static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr) { diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 7978044200..02990fb102 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -301,6 +301,7 @@ return { strwidth={args=1}, submatch={args={1, 2}}, substitute={args=4}, + swapinfo={args={1}}, synID={args=3}, synIDattr={args={2, 3}}, synIDtrans={args=1}, diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 662eda3c7c..3e18c8559a 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -1457,10 +1457,41 @@ static char *make_percent_swname(const char *dir, char *name) 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 (b0_magic_wrong(&b0)) { + tv_dict_add_str(d, S_LEN("error"), xstrdup("magic number mismatch")); + } else { + // We have swap information. + tv_dict_add_str(d, S_LEN("version"), xstrdup((char *)b0.b0_version)); + tv_dict_add_str(d, S_LEN("user"), xstrdup((char *)b0.b0_uname)); + tv_dict_add_str(d, S_LEN("host"), xstrdup((char *)b0.b0_hname)); + tv_dict_add_str(d, S_LEN("fname"), xstrdup((char *)b0.b0_fname)); + + 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)); +#ifdef CHECK_INODE + tv_dict_add_nr(d, S_LEN("inode"), char_to_long(b0.b0_ino)); +#endif + } + } else { + tv_dict_add_str(d, S_LEN("error"), xstrdup("Cannot read file")); + } + close(fd); + } else { + tv_dict_add_str(d, S_LEN("error"), xstrdup("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); diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim index bc7b7c00d3..28f995f6fb 100644 --- a/src/nvim/testdir/test_swap.vim +++ b/src/nvim/testdir/test_swap.vim @@ -61,3 +61,37 @@ func Test_missing_dir() set directory& call delete('Xswapdir', 'rf') endfunc + +func Test_swapinfo() + new Xswapinfo + call setline(1, ['one', 'two', 'three']) + w + let fname = trim(execute('swapname')) + call assert_match('Xswapinfo', fname) + let info = swapinfo(fname) + call assert_match('8\.', info.version) + call assert_match('\w', info.user) + call assert_equal(hostname(), info.host) + call assert_match('Xswapinfo', info.fname) + call assert_equal(getpid(), info.pid) + call assert_match('^\d*$', info.mtime) + if has_key(info, 'inode') + call assert_match('\d', info.inode) + endif + bwipe! + call delete(fname) + call delete('Xswapinfo') + + let info = swapinfo('doesnotexist') + call assert_equal('Cannot open file', info.error) + + call writefile(['burp'], 'Xnotaswapfile') + let info = swapinfo('Xnotaswapfile') + call assert_equal('Cannot read file', info.error) + call delete('Xnotaswapfile') + + call writefile([repeat('x', 10000)], 'Xnotaswapfile') + let info = swapinfo('Xnotaswapfile') + call assert_equal('magic number mismatch', info.error) + call delete('Xnotaswapfile') +endfunc -- cgit From 39e3783c697bad7ed16f5ce53910bb55f412626f Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 29 Apr 2019 19:59:54 +0200 Subject: vim-patch:8.1.0314: add swapinfo() "dirty" item Problem: Build failure without the +eval feature. (Brenton Horne) Solution: Add #ifdef. Also add the "dirty" item. https://github.com/vim/vim/commit/47ad5656e1e4285a74e7e8e5d0f1f71cd554e25c --- runtime/doc/eval.txt | 4 +++- src/nvim/memline.c | 9 +++++---- src/nvim/testdir/test_swap.vim | 3 ++- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 0472a67d18..3553174d9f 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -7770,10 +7770,12 @@ swapinfo({fname}) swapinfo() file mtime last modification time in seconds inode Optional: INODE number of the file + dirty 1 if file was modified, 0 if not In case of failure an "error" item is added with the reason: Cannot open file: file not found or in accessible Cannot read file: cannot read first block - magic number mismatch: info in first block is invalid + Not a swap file: does not contain correct block ID + Magic number mismatch: Info in first block is invalid synID({lnum}, {col}, {trans}) *synID()* The result is a Number, which is the syntax ID at the position diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 3e18c8559a..cbe4ce625a 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -1466,8 +1466,10 @@ void get_b0_dict(const char *fname, dict_T *d) if ((fd = os_open(fname, O_RDONLY, 0)) >= 0) { if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0)) { - if (b0_magic_wrong(&b0)) { - tv_dict_add_str(d, S_LEN("error"), xstrdup("magic number mismatch")); + if (ml_check_b0_id(&b0) == FAIL) { + tv_dict_add_str(d, S_LEN("error"), xstrdup("Not a swap file")); + } else if (b0_magic_wrong(&b0)) { + tv_dict_add_str(d, S_LEN("error"), xstrdup("Magic number mismatch")); } else { // We have swap information. tv_dict_add_str(d, S_LEN("version"), xstrdup((char *)b0.b0_version)); @@ -1477,9 +1479,8 @@ void get_b0_dict(const char *fname, dict_T *d) 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)); -#ifdef CHECK_INODE + 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)); -#endif } } else { tv_dict_add_str(d, S_LEN("error"), xstrdup("Cannot read file")); diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim index 28f995f6fb..241754aae0 100644 --- a/src/nvim/testdir/test_swap.vim +++ b/src/nvim/testdir/test_swap.vim @@ -73,6 +73,7 @@ func Test_swapinfo() call assert_match('\w', info.user) call assert_equal(hostname(), info.host) call assert_match('Xswapinfo', info.fname) + call assert_match(0, info.dirty) call assert_equal(getpid(), info.pid) call assert_match('^\d*$', info.mtime) if has_key(info, 'inode') @@ -92,6 +93,6 @@ func Test_swapinfo() call writefile([repeat('x', 10000)], 'Xnotaswapfile') let info = swapinfo('Xnotaswapfile') - call assert_equal('magic number mismatch', info.error) + call assert_equal('Not a swap file', info.error) call delete('Xnotaswapfile') endfunc -- cgit From 3a92040b443edfbf0f7c9423c865e5a013b07d62 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 29 Apr 2019 21:09:14 +0200 Subject: vim-patch:8.1.0316: swapinfo() test fails on Travis Problem: swapinfo() test fails on Travis. Solution: Handle a long host name. (Ozaki Kiichi, closes vim/vim#3361) Also make the version check flexible. (James McCoy) https://github.com/vim/vim/commit/4c5765bc47b2708075e8d8471e8d342079479777 --- src/nvim/testdir/test_swap.vim | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim index 241754aae0..789f14aa8d 100644 --- a/src/nvim/testdir/test_swap.vim +++ b/src/nvim/testdir/test_swap.vim @@ -69,9 +69,13 @@ func Test_swapinfo() let fname = trim(execute('swapname')) call assert_match('Xswapinfo', fname) let info = swapinfo(fname) - call assert_match('8\.', info.version) + + let ver = printf('VIM %d.%d', v:version / 100, v:version % 100) + call assert_equal(ver, info.version) + call assert_match('\w', info.user) - call assert_equal(hostname(), info.host) + " host name is truncated to 39 bytes in the swap file + call assert_equal(hostname()[:38], info.host) call assert_match('Xswapinfo', info.fname) call assert_match(0, info.dirty) call assert_equal(getpid(), info.pid) -- cgit From 035a41c21827e616ec2abba581329bea4201da88 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 29 Apr 2019 21:12:42 +0200 Subject: vim-patch:8.1.0325: strings in swap file may not be NUL terminated Problem: Strings in swap file may not be NUL terminated. (Coverity) Solution: Limit the length of the used string. https://github.com/vim/vim/commit/7c60505e1012a43549c2c075c27463c5399e81ec --- src/nvim/memline.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/nvim/memline.c b/src/nvim/memline.c index cbe4ce625a..7e15627292 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -1472,10 +1472,14 @@ void get_b0_dict(const char *fname, dict_T *d) tv_dict_add_str(d, S_LEN("error"), xstrdup("Magic number mismatch")); } else { // We have swap information. - tv_dict_add_str(d, S_LEN("version"), xstrdup((char *)b0.b0_version)); - tv_dict_add_str(d, S_LEN("user"), xstrdup((char *)b0.b0_uname)); - tv_dict_add_str(d, S_LEN("host"), xstrdup((char *)b0.b0_hname)); - tv_dict_add_str(d, S_LEN("fname"), xstrdup((char *)b0.b0_fname)); + tv_dict_add_str(d, S_LEN("version"), + xstrndup((char *)b0.b0_version, 10)); + tv_dict_add_str(d, S_LEN("user"), + xstrndup((char *)b0.b0_uname, B0_UNAME_SIZE)); + tv_dict_add_str(d, S_LEN("host"), + xstrndup((char *)b0.b0_hname, B0_HNAME_SIZE)); + tv_dict_add_str(d, S_LEN("fname"), + xstrndup((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)); -- cgit From 04f0bc97b732c395dbfcc9411910ab58b9346f58 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 29 Apr 2019 19:39:18 +0200 Subject: vim-patch:8.1.0401: can't get swap name of another buffer Problem: Can't get swap name of another buffer. Solution: Add swapname(). (Ozaki Kiichi, closes vim/vim#3441) https://github.com/vim/vim/commit/110bd60985c31e8978e9b071e2179f4233ef8557 --- runtime/doc/eval.txt | 8 ++++++++ src/nvim/eval.c | 14 ++++++++++++++ src/nvim/eval.lua | 1 + src/nvim/testdir/test_swap.vim | 33 +++++++++++++++++++++++++++++---- 4 files changed, 52 insertions(+), 4 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 3553174d9f..56e0a57816 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2322,6 +2322,7 @@ submatch({nr} [, {list}]) String or List substitute({expr}, {pat}, {sub}, {flags}) String all {pat} in {expr} replaced with {sub} swapinfo({fname}) Dict information about swap file {fname} +swapname({expr}) String swap file of buffer {expr} synID({lnum}, {col}, {trans}) Number syntax ID at {lnum} and {col} synIDattr({synID}, {what} [, {mode}]) String attribute {what} of syntax ID {synID} @@ -7777,6 +7778,13 @@ swapinfo({fname}) swapinfo() Not a swap file: does not contain correct block ID Magic number mismatch: Info in first block is invalid +swapname({expr}) *swapname()* + The result is the swap file path of the buffer {expr}. + For the use of {expr}, see |bufname()| above. + If buffer {expr} is the current buffer, the result is equal to + |:swapname| (unless no swap file). + If buffer {expr} has no swap file, returns an empty string. + synID({lnum}, {col}, {trans}) *synID()* The result is a Number, which is the syntax ID at the position {lnum} and {col} in the current window. diff --git a/src/nvim/eval.c b/src/nvim/eval.c index e9da1f2f2b..52b0f6cd8a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -16430,6 +16430,20 @@ static void f_swapinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) get_b0_dict(tv_get_string(argvars), rettv->vval.v_dict); } +/// "swapname(expr)" function +static void f_swapname(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + rettv->v_type = VAR_STRING; + buf_T *buf = tv_get_buf(&argvars[0], false); + if (buf == NULL + || buf->b_ml.ml_mfp == NULL + || buf->b_ml.ml_mfp->mf_fname == NULL) { + rettv->vval.v_string = NULL; + } else { + rettv->vval.v_string = vim_strsave(buf->b_ml.ml_mfp->mf_fname); + } +} + /// "synID(lnum, col, trans)" function static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr) { diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 02990fb102..b3f5ef2935 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -302,6 +302,7 @@ return { submatch={args={1, 2}}, substitute={args=4}, swapinfo={args={1}}, + swapname={args={1}}, synID={args=3}, synIDattr={args={2, 3}}, synIDtrans={args=1}, diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim index 789f14aa8d..1bd73707a8 100644 --- a/src/nvim/testdir/test_swap.vim +++ b/src/nvim/testdir/test_swap.vim @@ -1,5 +1,9 @@ " Tests for the swap feature +func s:swapname() + return trim(execute('swapname')) +endfunc + " Tests for 'directory' option. func Test_swap_directory() if !has("unix") @@ -17,7 +21,7 @@ func Test_swap_directory() " Verify that the swap file doesn't exist in the current directory call assert_equal([], glob(".Xtest1*.swp", 1, 1, 1)) edit Xtest1 - let swfname = split(execute("swapname"))[0] + let swfname = s:swapname() call assert_equal([swfname], glob(swfname, 1, 1, 1)) " './dir', swap file in a directory relative to the file @@ -27,7 +31,7 @@ func Test_swap_directory() edit Xtest1 call assert_equal([], glob(swfname, 1, 1, 1)) let swfname = "Xtest2/Xtest1.swp" - call assert_equal(swfname, split(execute("swapname"))[0]) + call assert_equal(swfname, s:swapname()) call assert_equal([swfname], glob("Xtest2/*", 1, 1, 1)) " 'dir', swap file in directory relative to the current dir @@ -38,7 +42,7 @@ func Test_swap_directory() edit Xtest2/Xtest3 call assert_equal(["Xtest2/Xtest3"], glob("Xtest2/*", 1, 1, 1)) let swfname = "Xtest.je/Xtest3.swp" - call assert_equal(swfname, split(execute("swapname"))[0]) + call assert_equal(swfname, s:swapname()) call assert_equal([swfname], glob("Xtest.je/*", 1, 1, 1)) set dir& @@ -66,7 +70,7 @@ func Test_swapinfo() new Xswapinfo call setline(1, ['one', 'two', 'three']) w - let fname = trim(execute('swapname')) + let fname = s:swapname() call assert_match('Xswapinfo', fname) let info = swapinfo(fname) @@ -100,3 +104,24 @@ func Test_swapinfo() call assert_equal('Not a swap file', info.error) call delete('Xnotaswapfile') endfunc + +func Test_swapname() + edit Xtest1 + let expected = s:swapname() + call assert_equal(expected, swapname('%')) + + new Xtest2 + let buf = bufnr('%') + let expected = s:swapname() + wincmd p + call assert_equal(expected, swapname(buf)) + + new Xtest3 + setlocal noswapfile + call assert_equal('', swapname('%')) + + bwipe! + call delete('Xtest1') + call delete('Xtest2') + call delete('Xtest3') +endfunc -- cgit From 3033b5a70a1a9f1fdce1a772541fec9722825298 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 29 Apr 2019 17:41:17 +0200 Subject: vim-patch:8.1.1231: asking about existing swap file unnecessarily Problem: Asking about existing swap file unnecessarily. Solution: When it is safe, delete the swap file. Remove HAS_SWAP_EXISTS_ACTION, it is always defined. (closes vim/vim#1237) https://github.com/vim/vim/commit/67cf86bfff5fd5224d557d81cb146f46e33b831c N/A: vim-patch:8.1.1232 vim-patch:8.1.1233 vim-patch:8.1.1236 --- runtime/doc/usr_11.txt | 7 ++++ src/nvim/fileio.c | 2 +- src/nvim/main.c | 7 ++-- src/nvim/memline.c | 86 ++++++++++++++++++++++++++++++++---------- src/nvim/os/process.c | 6 +++ src/nvim/testdir/test_swap.vim | 56 +++++++++++++++++++++++++++ 6 files changed, 140 insertions(+), 24 deletions(-) diff --git a/runtime/doc/usr_11.txt b/runtime/doc/usr_11.txt index e5591ac1d1..42b564e962 100644 --- a/runtime/doc/usr_11.txt +++ b/runtime/doc/usr_11.txt @@ -205,6 +205,13 @@ something wrong. It may be one of these two situations. NEWER than swap file! ~ +NOTE that in the following situation Vim knows the swap file is not useful and +will automatically delete it: +- The file is a valid swap file (Magic number is correct). +- The flag that the file was modified is not set. +- The process is not running. + + UNREADABLE SWAP FILE Sometimes the line diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index b2840c9402..7abc75916c 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -633,7 +633,7 @@ readfile ( #endif } - /* If "Quit" selected at ATTENTION dialog, don't load the file */ + // If "Quit" selected at ATTENTION dialog, don't load the file. if (swap_exists_action == SEA_QUIT) { if (!read_buffer && !read_stdin) close(fd); diff --git a/src/nvim/main.c b/src/nvim/main.c index 27e2abe1ad..4e1c7dff57 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -1520,10 +1520,11 @@ static void create_windows(mparm_T *parmp) dorewind = FALSE; curbuf = curwin->w_buffer; if (curbuf->b_ml.ml_mfp == NULL) { - /* Set 'foldlevel' to 'foldlevelstart' if it's not negative. */ - if (p_fdls >= 0) + // Set 'foldlevel' to 'foldlevelstart' if it's not negative.. + if (p_fdls >= 0) { curwin->w_p_fdl = p_fdls; - /* When getting the ATTENTION prompt here, use a dialog */ + } + // When getting the ATTENTION prompt here, use a dialog. swap_exists_action = SEA_DIALOG; set_buflisted(TRUE); diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 7e15627292..23f223fcac 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 */ @@ -1453,9 +1454,7 @@ static char *make_percent_swname(const char *dir, char *name) return d; } -#ifdef UNIX static bool process_still_running; -#endif /// Return information found in swapfile "fname" in dictionary "d". /// This is used by the swapinfo() function. @@ -1566,12 +1565,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)) { @@ -1588,6 +1585,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: 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 { @@ -3389,17 +3431,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)) + // 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. @@ -3431,21 +3480,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 diff --git a/src/nvim/os/process.c b/src/nvim/os/process.c index a67e7487eb..a1020be215 100644 --- a/src/nvim/os/process.c +++ b/src/nvim/os/process.c @@ -265,3 +265,9 @@ Dictionary os_proc_info(int pid) return pinfo; } #endif + +/// Return true if process `pid` is running. +bool os_proc_running(int pid) +{ + return uv_kill(pid, 0) == 0; +} diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim index 1bd73707a8..1000422813 100644 --- a/src/nvim/testdir/test_swap.vim +++ b/src/nvim/testdir/test_swap.vim @@ -125,3 +125,59 @@ func Test_swapname() call delete('Xtest2') call delete('Xtest3') endfunc + +func Test_swapfile_delete() + autocmd! SwapExists + function s:swap_exists() + let v:swapchoice = s:swap_choice + let s:swapname = v:swapname + let s:filename = expand('') + endfunc + augroup test_swapfile_delete + autocmd! + autocmd SwapExists * call s:swap_exists() + augroup END + + + " Create a valid swapfile by editing a file. + split XswapfileText + call setline(1, ['one', 'two', 'three']) + write " file is written, not modified + " read the swapfile as a Blob + let swapfile_name = swapname('%') + let swapfile_bytes = readfile(swapfile_name, 'B') + + " Close the file and recreate the swap file. + " Now editing the file will run into the process still existing + quit + call writefile(swapfile_bytes, swapfile_name) + let s:swap_choice = 'e' + let s:swapname = '' + split XswapfileText + quit + call assert_equal(swapfile_name, s:swapname) + + " Write the swapfile with a modified PID, now it will be automatically + " deleted. Process one should never be Vim. + let swapfile_bytes[24:27] = 0z01000000 + call writefile(swapfile_bytes, swapfile_name) + let s:swapname = '' + split XswapfileText + quit + call assert_equal('', s:swapname) + + " Now set the modified flag, the swap file will not be deleted + let swapfile_bytes[28 + 80 + 899] = 0x55 + call writefile(swapfile_bytes, swapfile_name) + let s:swapname = '' + split XswapfileText + quit + call assert_equal(swapfile_name, s:swapname) + + call delete('XswapfileText') + call delete(swapfile_name) + augroup test_swapfile_delete + autocmd! + augroup END + augroup! test_swapfile_delete +endfunc -- cgit From 8cf7daaaa8b16522cddf0d22ab35d28833b8dbbf Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 29 Apr 2019 19:18:40 +0200 Subject: verb_msg: remove char_u --- src/nvim/eval.c | 24 +++++++++--------------- src/nvim/message.c | 10 +++------- src/nvim/undo.c | 2 +- 3 files changed, 13 insertions(+), 23 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 52b0f6cd8a..4d66e34a70 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5221,7 +5221,7 @@ bool garbage_collect(bool testing) (void)garbage_collect(testing); } } else if (p_verbose > 0) { - verb_msg((char_u *)_( + verb_msg(_( "Not enough memory to set references, garbage collection aborted!")); } #undef ABORTING @@ -11474,25 +11474,21 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) static garray_T ga_userinput = {0, 0, sizeof(tasave_T), 4, NULL}; -/* - * "inputrestore()" function - */ +/// "inputrestore()" function static void f_inputrestore(typval_T *argvars, typval_T *rettv, FunPtr fptr) { if (!GA_EMPTY(&ga_userinput)) { - --ga_userinput.ga_len; + ga_userinput.ga_len--; restore_typeahead((tasave_T *)(ga_userinput.ga_data) - + ga_userinput.ga_len); - /* default return is zero == OK */ + + ga_userinput.ga_len); + // default return is zero == OK } else if (p_verbose > 1) { - verb_msg((char_u *)_("called inputrestore() more often than inputsave()")); - rettv->vval.v_number = 1; /* Failed */ + verb_msg(_("called inputrestore() more often than inputsave()")); + rettv->vval.v_number = 1; // Failed } } -/* - * "inputsave()" function - */ +/// "inputsave()" function static void f_inputsave(typval_T *argvars, typval_T *rettv, FunPtr fptr) { // Add an entry to the stack of typeahead storage. @@ -11500,9 +11496,7 @@ static void f_inputsave(typval_T *argvars, typval_T *rettv, FunPtr fptr) save_typeahead(p); } -/* - * "inputsecret()" function - */ +/// "inputsecret()" function static void f_inputsecret(typval_T *argvars, typval_T *rettv, FunPtr fptr) { cmdline_star++; diff --git a/src/nvim/message.c b/src/nvim/message.c index f7c248184e..28855402f4 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -133,15 +133,11 @@ int msg(char_u *s) return msg_attr_keep(s, 0, false, false); } -/* - * Like msg() but keep it silent when 'verbosefile' is set. - */ -int verb_msg(char_u *s) +/// Like msg() but keep it silent when 'verbosefile' is set. +int verb_msg(char *s) { - int n; - verbose_enter(); - n = msg_attr_keep(s, 0, false, false); + int n = msg_attr_keep((char_u *)s, 0, false, false); verbose_leave(); return n; diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 0a32d9b872..c531a488bc 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -1132,7 +1132,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, * existing undo file. */ if (buf->b_u_numhead == 0 && buf->b_u_line_ptr == NULL) { if (p_verbose > 0) - verb_msg((char_u *)_("Skipping undo file write, nothing to undo")); + verb_msg(_("Skipping undo file write, nothing to undo")); goto theend; } -- cgit From 7f8f6ac8d702a3acf59b4bbe754181dada2abade Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 29 Apr 2019 21:08:01 +0200 Subject: test/old: skip Test_swapfile_delete() until "blob" is merged --- src/nvim/testdir/test_swap.vim | 1 + 1 file changed, 1 insertion(+) diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim index 1000422813..c7e164ea00 100644 --- a/src/nvim/testdir/test_swap.vim +++ b/src/nvim/testdir/test_swap.vim @@ -127,6 +127,7 @@ func Test_swapname() endfunc func Test_swapfile_delete() + throw 'skipped: need the "blob" feature for this test' autocmd! SwapExists function s:swap_exists() let v:swapchoice = s:swap_choice -- cgit From 7032562faf9c9b44ca58284368937ad06b3a67fa Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 29 Apr 2019 21:21:30 +0200 Subject: vim-patch:8.1.1234: swap file test fails on MS-Windows Problem: Swap file test fails on MS-Windows. Solution: Only compare the tail of the file names. https://github.com/vim/vim/commit/701df4eb64ae9945c63f83621b54aa69e1336a79 --- src/nvim/testdir/test_swap.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim index c7e164ea00..4dc8803a2a 100644 --- a/src/nvim/testdir/test_swap.vim +++ b/src/nvim/testdir/test_swap.vim @@ -156,7 +156,7 @@ func Test_swapfile_delete() let s:swapname = '' split XswapfileText quit - call assert_equal(swapfile_name, s:swapname) + call assert_equal(fnamemodify(swapfile_name, ':t'), fnamemodify(s:swapname, ':t')) " Write the swapfile with a modified PID, now it will be automatically " deleted. Process one should never be Vim. @@ -173,7 +173,7 @@ func Test_swapfile_delete() let s:swapname = '' split XswapfileText quit - call assert_equal(swapfile_name, s:swapname) + call assert_equal(fnamemodify(swapfile_name, ':t'), fnamemodify(s:swapname, ':t')) call delete('XswapfileText') call delete(swapfile_name) -- cgit From a01246c275a9cc1462523a9f8b6be6369cdff70b Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 29 Apr 2019 21:37:22 +0200 Subject: lint --- src/nvim/memline.c | 6 +++--- src/nvim/undo.c | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 23f223fcac..e341ba0427 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -1623,7 +1623,7 @@ static time_t swapfile_unchanged(char *fname) ret = false; } - // TODO: Should we check if the swap file was created on the current + // TODO(bram): Should we check if the swap file was created on the current // system? And the current user? close(fd); @@ -3446,8 +3446,8 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, // 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); + && has_autocmd(EVENT_SWAPEXISTS, (char_u *)buf_fname, buf)) { + choice = do_swapexists(buf, (char_u *)fname); } if (choice == 0) { diff --git a/src/nvim/undo.c b/src/nvim/undo.c index c531a488bc..f5ee0ece8d 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -1131,8 +1131,9 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, /* If there is no undo information at all, quit here after deleting any * existing undo file. */ if (buf->b_u_numhead == 0 && buf->b_u_line_ptr == NULL) { - if (p_verbose > 0) + if (p_verbose > 0) { verb_msg(_("Skipping undo file write, nothing to undo")); + } goto theend; } -- cgit From aac731c22b94117aea2257d4f19fd1f0930a2385 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 29 Apr 2019 22:05:45 +0200 Subject: vim-patch:8.1.0642: swapinfo() leaks memory Problem: swapinfo() leaks memory. Solution: Avoid allocating the strings twice. https://github.com/vim/vim/commit/e6fdf79980c0f2856700d4f46de700293f477429 --- src/nvim/eval/typval.c | 28 ++++++++++++++++++++++------ src/nvim/memline.c | 23 +++++++++++------------ 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 912aecafec..ffb46abfea 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1656,12 +1656,7 @@ int tv_dict_add_special(dict_T *const d, const char *const key, /// Add a string entry to dictionary /// -/// @param[out] d Dictionary to add entry to. -/// @param[in] key Key to add. -/// @param[in] key_len Key length. -/// @param[in] val String to add. -/// -/// @return OK in case of success, FAIL when key already exists. +/// @see tv_dict_add_allocated_str int tv_dict_add_str(dict_T *const d, const char *const key, const size_t key_len, const char *const val) @@ -1670,6 +1665,27 @@ int tv_dict_add_str(dict_T *const d, return tv_dict_add_allocated_str(d, key, key_len, xstrdup(val)); } +/// Add a string entry to dictionary +/// +/// @param[out] d Dictionary to add entry to. +/// @param[in] key Key to add. +/// @param[in] key_len Key length. +/// @param[in] val String to add. NULL adds empty string. +/// @param[in] len Use this many bytes from `val`, or -1 for whole string. +/// +/// @return OK in case of success, FAIL when key already exists. +int tv_dict_add_str_len(dict_T *const d, + const char *const key, const size_t key_len, + char *const val, int len) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + char *s = val ? val : ""; + if (val != NULL) { + s = (len < 0) ? xstrdup(val) : xstrndup(val, (size_t)len); + } + return tv_dict_add_allocated_str(d, key, key_len, s); +} + /// Add a string entry to dictionary /// /// Unlike tv_dict_add_str() saves val to the new dictionary item in place of diff --git a/src/nvim/memline.c b/src/nvim/memline.c index e341ba0427..25266f4261 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -1466,19 +1466,18 @@ void get_b0_dict(const char *fname, dict_T *d) 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"), xstrdup("Not a swap file")); + 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"), xstrdup("Magic number mismatch")); + tv_dict_add_str(d, S_LEN("error"), "Magic number mismatch"); } else { // We have swap information. - tv_dict_add_str(d, S_LEN("version"), - xstrndup((char *)b0.b0_version, 10)); - tv_dict_add_str(d, S_LEN("user"), - xstrndup((char *)b0.b0_uname, B0_UNAME_SIZE)); - tv_dict_add_str(d, S_LEN("host"), - xstrndup((char *)b0.b0_hname, B0_HNAME_SIZE)); - tv_dict_add_str(d, S_LEN("fname"), - xstrndup((char *)b0.b0_fname, B0_FNAME_SIZE_ORG)); + 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)); @@ -1486,11 +1485,11 @@ void get_b0_dict(const char *fname, dict_T *d) tv_dict_add_nr(d, S_LEN("inode"), char_to_long(b0.b0_ino)); } } else { - tv_dict_add_str(d, S_LEN("error"), xstrdup("Cannot read file")); + tv_dict_add_str(d, S_LEN("error"), "Cannot read file"); } close(fd); } else { - tv_dict_add_str(d, S_LEN("error"), xstrdup("Cannot open file")); + tv_dict_add_str(d, S_LEN("error"), "Cannot open file"); } } -- cgit