aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/eval.txt17
-rw-r--r--src/nvim/eval.c7
-rw-r--r--src/nvim/eval.lua1
-rw-r--r--src/nvim/memline.c39
-rw-r--r--src/nvim/testdir/test_swap.vim34
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