aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin M. Keyes <justinkz@gmail.com>2017-02-02 13:16:15 +0100
committerJustin M. Keyes <justinkz@gmail.com>2017-02-04 11:07:49 +0100
commit224f99b85d311ebd31451db13b66e4a3c7e51938 (patch)
treede2bb4bc8f06e09ede603ae535697384133a4ed5
parent7d58aba80c6d81a9af40f54e566c0cdcea2de3e3 (diff)
downloadrneovim-224f99b85d311ebd31451db13b66e4a3c7e51938.tar.gz
rneovim-224f99b85d311ebd31451db13b66e4a3c7e51938.tar.bz2
rneovim-224f99b85d311ebd31451db13b66e4a3c7e51938.zip
win: Append process dir to $PATH
This allows executables to be found by :!, system(), and executable() if they live next to ("sibling" to) nvim.exe. This is what gvim on Windows does, and also matches the behavior of Win32 SearchPath(). https://github.com/vim/vim/blob/c4a249a736d40ec54794827ef95804c225d0e38f/src/os_win32.c#L354-L370
-rw-r--r--runtime/doc/eval.txt17
-rw-r--r--runtime/doc/vim_diff.txt3
-rw-r--r--src/nvim/main.c28
-rw-r--r--src/nvim/os/env.c40
-rw-r--r--src/nvim/os/fs.c8
-rw-r--r--src/nvim/os/unix_defs.h3
-rw-r--r--src/nvim/os/win_defs.h3
-rw-r--r--test/functional/eval/executable_spec.lua25
-rw-r--r--test/functional/options/defaults_spec.lua4
-rw-r--r--test/unit/os/env_spec.lua20
10 files changed, 120 insertions, 31 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 9aa60657e0..b729519d93 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -1686,11 +1686,7 @@ v:progname Contains the name (with path removed) with which Nvim was
Read-only.
*v:progpath* *progpath-variable*
-v:progpath Contains the command with which Vim was invoked, including the
- path. To get the full path use: >
- echo exepath(v:progpath)
-< NOTE: This does not work when the command is a relative path
- and the current directory has changed.
+v:progpath Absolute path to the current running Nvim.
Read-only.
*v:register* *register-variable*
@@ -3104,13 +3100,10 @@ execute({command} [, {silent}]) *execute()*
Note: Text attributes (highlights) are not captured.
exepath({expr}) *exepath()*
- If {expr} is an executable and is either an absolute path, a
- relative path or found in $PATH, return the full path.
- Note that the current directory is used when {expr} starts
- with "./", which may be a problem for Vim: >
- echo exepath(v:progpath)
-< If {expr} cannot be found in $PATH or is not executable then
- an empty string is returned.
+ Returns the full path of {expr} if it is an executable and
+ given as a (partial or full) path or is found in $PATH.
+ Returns empty string otherwise.
+ If {expr} starts with "./" the |current-directory| is used.
*exists()*
exists({expr}) The result is a Number, which is non-zero if {expr} is
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index 20002c1118..de93aab399 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -108,12 +108,13 @@ Some `CTRL-SHIFT-...` key chords are distinguished from `CTRL-...` variants
<C-Tab>, <C-S-Tab>, <C-BS>, <C-S-BS>, <C-Enter>, <C-S-Enter>
Options:
- 'inccommand' shows results while typing a |:substitute| command
+ 'inccommand' shows interactive results for |:substitute|-like commands
'statusline' supports unlimited alignment sections
'tabline' %@Func@foo%X can call any function on mouse-click
Variables:
|v:event|
+ |v:progpath| is always absolute ("full")
|v:windowid| is always available (for use by external UIs)
Commands:
diff --git a/src/nvim/main.c b/src/nvim/main.c
index c7a60d07c1..6194e5f948 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -238,9 +238,7 @@ int main(int argc, char **argv)
// Check if we have an interactive window.
check_and_set_isatty(&params);
- // Get the name with which Nvim was invoked, with and without path.
- set_vim_var_string(VV_PROGPATH, argv[0], -1);
- set_vim_var_string(VV_PROGNAME, (char *) path_tail((char_u *) argv[0]), -1);
+ init_path(argv[0]);
event_init();
/*
@@ -1194,9 +1192,27 @@ static void check_and_set_isatty(mparm_T *paramp)
paramp->err_isatty = os_isatty(fileno(stderr));
TIME_MSG("window checked");
}
-/*
- * Get filename from command line, given that there is one.
- */
+
+// Sets v:progname and v:progpath. Also modifies $PATH on Windows.
+static void init_path(char *exename)
+{
+ char exepath[MAXPATHL] = { 0 };
+ size_t exepathlen = MAXPATHL;
+ // Make v:progpath absolute.
+ if (os_exepath(exepath, &exepathlen) != 0) {
+ EMSG2(e_intern2, "init_path()");
+ }
+ set_vim_var_string(VV_PROGPATH, exepath, -1);
+ set_vim_var_string(VV_PROGNAME, (char *)path_tail((char_u *)exename), -1);
+
+#ifdef WIN32
+ // Append the process start directory to $PATH, so that ":!foo" finds tools
+ // shipped with Windows package. This also mimics SearchPath().
+ os_setenv_append_path(exepath);
+#endif
+}
+
+/// Get filename from command line, if any.
static char_u *get_fname(mparm_T *parmp, char_u *cwd)
{
#if !defined(UNIX)
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index 747a34d8ce..4707b0a326 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -831,3 +831,43 @@ char_u *get_env_name(expand_T *xp, int idx)
return NULL;
}
+/// Appends the head of `fname` to $PATH and sets it in the environment.
+///
+/// @param fname Full path whose parent directory will be appended to $PATH.
+///
+/// @return true if `path` was appended-to
+bool os_setenv_append_path(const char *fname)
+ FUNC_ATTR_NONNULL_ALL
+{
+#ifdef WIN32
+// 8191 (plus NUL) is considered the practical maximum.
+# define MAX_ENVPATHLEN 8192
+#else
+// No prescribed maximum on unix.
+# define MAX_ENVPATHLEN INT_MAX
+#endif
+ if (!path_is_absolute_path((char_u *)fname)) {
+ EMSG2(_(e_intern2), "os_setenv_append_path()");
+ return false;
+ }
+ const char *tail = (char *)path_tail_with_sep((char_u *)fname);
+ const char *dir = (char *)vim_strnsave((char_u *)fname,
+ (size_t)(tail - fname));
+ const char *path = os_getenv("PATH");
+ const size_t pathlen = path ? strlen(path) : 0;
+ const size_t newlen = pathlen + strlen(dir) + 2;
+ if (newlen < MAX_ENVPATHLEN) {
+ char *temp = xmalloc(newlen);
+ if (pathlen == 0) {
+ temp[0] = NUL;
+ } else {
+ xstrlcpy(temp, path, newlen);
+ xstrlcat(temp, ENV_SEPSTR, newlen);
+ }
+ xstrlcat(temp, dir, newlen);
+ os_setenv("PATH", temp, 1);
+ xfree(temp);
+ return true;
+ }
+ return false;
+}
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index b3d838c01a..4ca67d1f1a 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -224,7 +224,7 @@ bool os_can_exe(const char_u *name, char_u **abspath, bool use_path)
if (!use_path || path_is_absolute_path(name)
|| (name[0] == '.'
&& (name[1] == '/' || (name[1] == '.' && name[2] == '/')))) {
-#if WIN32
+#ifdef WIN32
bool ok = is_executable(name);
#else
// Must have path separator, cannot execute files in the current directory.
@@ -255,7 +255,7 @@ static bool is_executable(const char_u *name)
return false;
}
-#if WIN32
+#ifdef WIN32
// Windows does not have exec bit; just check if the file exists and is not
// a directory.
return (S_ISREG(mode));
@@ -281,7 +281,7 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath)
#ifdef WIN32
// Prepend ".;" to $PATH.
size_t pathlen = strlen(path_env);
- char *path = memcpy(xmallocz(pathlen + 3), ".;", 2);
+ char *path = memcpy(xmallocz(pathlen + 3), "." ENV_SEPSTR, 2);
memcpy(path + 2, path_env, pathlen);
#else
char *path = xstrdup(path_env);
@@ -1027,7 +1027,7 @@ shortcut_end:
int os_translate_sys_error(int sys_errno) {
#ifdef HAVE_UV_TRANSLATE_SYS_ERROR
return uv_translate_sys_error(sys_errno);
-#elif WIN32
+#elif defined(WIN32)
// TODO(equalsraf): libuv does not yet expose uv_translate_sys_error()
// in its public API, include a version here until it can be used.
// See https://github.com/libuv/libuv/issues/79
diff --git a/src/nvim/os/unix_defs.h b/src/nvim/os/unix_defs.h
index 690a39c3cd..c98aa88bfa 100644
--- a/src/nvim/os/unix_defs.h
+++ b/src/nvim/os/unix_defs.h
@@ -16,7 +16,8 @@
// Special wildcards that need to be handled by the shell.
#define SPECIAL_WILDCHAR "`'{"
-// Separator character for environment variables.
+// Character that separates entries in $PATH.
#define ENV_SEPCHAR ':'
+#define ENV_SEPSTR ":"
#endif // NVIM_OS_UNIX_DEFS_H
diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h
index 6a29f86e79..8de896c490 100644
--- a/src/nvim/os/win_defs.h
+++ b/src/nvim/os/win_defs.h
@@ -20,8 +20,9 @@
#define FNAME_ILLEGAL "\"*?><|"
-// Separator character for environment variables.
+// Character that separates entries in $PATH.
#define ENV_SEPCHAR ';'
+#define ENV_SEPSTR ";"
#define USE_CRNL
diff --git a/test/functional/eval/executable_spec.lua b/test/functional/eval/executable_spec.lua
index dd8861a07c..bcf5eba4eb 100644
--- a/test/functional/eval/executable_spec.lua
+++ b/test/functional/eval/executable_spec.lua
@@ -1,7 +1,6 @@
local helpers = require('test.functional.helpers')(after_each)
-local eq, clear, execute, call, iswin, write_file =
- helpers.eq, helpers.clear, helpers.execute, helpers.call, helpers.iswin,
- helpers.write_file
+local eq, clear, call, iswin, write_file =
+ helpers.eq, helpers.clear, helpers.call, helpers.iswin, helpers.write_file
describe('executable()', function()
before_each(clear)
@@ -15,8 +14,28 @@ describe('executable()', function()
eq(0, call('executable', 'no_such_file_exists_209ufq23f'))
end)
+ it('sibling to nvim binary', function()
+ -- Some executable in build/bin/, *not* in $PATH nor CWD.
+ local sibling_exe = 'printargs-test'
+ -- Windows: siblings are in Nvim's "pseudo-$PATH".
+ local expected = iswin() and 1 or 0
+ if iswin() then
+ print('XXXXXXXXXXXXXXXXXXXXXXXXX')
+ print(helpers.eval('$PATH'))
+ print('XXXXXXXXXXXXXXXXXXXXXXXXX')
+ -- $PATH on AppVeyor CI might be oversized, redefine it to a minimal one.
+ clear({env={PATH=[[C:\Windows\system32;C:\Windows]]}})
+ print(helpers.eval('$PATH'))
+ print('XXXXXXXXXXXXXXXXXXXXXXXXX')
+ eq('arg1=lemon;arg2=sky;arg3=tree;',
+ call('system', sibling_exe..' lemon sky tree'))
+ end
+ eq(expected, call('executable', sibling_exe))
+ end)
+
describe('exec-bit', function()
setup(function()
+ clear()
write_file('Xtest_not_executable', 'non-executable file')
write_file('Xtest_executable', 'executable file (exec-bit set)')
if not iswin() then -- N/A for Windows.
diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua
index caeca5e4e2..f3328886b5 100644
--- a/test/functional/options/defaults_spec.lua
+++ b/test/functional/options/defaults_spec.lua
@@ -96,6 +96,10 @@ describe('startup defaults', function()
eq(meths.get_option('runtimepath'), meths.get_option('packpath'))
end)
end)
+
+ it('v:progpath is set to the absolute path', function()
+ eq(eval("fnamemodify(v:progpath, ':p')"), eval('v:progpath'))
+ end)
end)
describe('XDG-based defaults', function()
diff --git a/test/unit/os/env_spec.lua b/test/unit/os/env_spec.lua
index 64bbaaa8c2..3c2cc164c9 100644
--- a/test/unit/os/env_spec.lua
+++ b/test/unit/os/env_spec.lua
@@ -14,15 +14,15 @@ local cimp = cimport('./src/nvim/os/os.h')
describe('env function', function()
local function os_setenv(name, value, override)
- return cimp.os_setenv((to_cstr(name)), (to_cstr(value)), override)
+ return cimp.os_setenv(to_cstr(name), to_cstr(value), override)
end
local function os_unsetenv(name, _, _)
- return cimp.os_unsetenv((to_cstr(name)))
+ return cimp.os_unsetenv(to_cstr(name))
end
local function os_getenv(name)
- local rval = cimp.os_getenv((to_cstr(name)))
+ local rval = cimp.os_getenv(to_cstr(name))
if rval ~= NULL then
return ffi.string(rval)
else
@@ -52,6 +52,20 @@ describe('env function', function()
end)
end)
+ describe('os_setenv_append_path', function()
+ it('appends /foo/bar to $PATH', function()
+ local original_path = os.getenv('PATH')
+ eq(true, cimp.os_setenv_append_path(to_cstr('/foo/bar/baz')))
+ eq(original_path..':/foo/bar', os.getenv('PATH'))
+ end)
+
+ it('returns false if `fname` is not absolute', function()
+ local original_path = os.getenv('PATH')
+ eq(false, cimp.os_setenv_append_path(to_cstr('foo/bar/baz')))
+ eq(original_path, os.getenv('PATH'))
+ end)
+ end)
+
describe('os_getenv', function()
it('reads an env variable', function()
local name = 'NEOVIM_UNIT_TEST_GETENV_1N'