mirror of
https://github.com/neovim/neovim.git
synced 2025-10-01 07:28:34 +00:00
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().
c4a249a736/src/os_win32.c (L354-L370)
This commit is contained in:
@@ -1686,11 +1686,7 @@ v:progname Contains the name (with path removed) with which Nvim was
|
|||||||
Read-only.
|
Read-only.
|
||||||
|
|
||||||
*v:progpath* *progpath-variable*
|
*v:progpath* *progpath-variable*
|
||||||
v:progpath Contains the command with which Vim was invoked, including the
|
v:progpath Absolute path to the current running Nvim.
|
||||||
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.
|
|
||||||
Read-only.
|
Read-only.
|
||||||
|
|
||||||
*v:register* *register-variable*
|
*v:register* *register-variable*
|
||||||
@@ -3104,13 +3100,10 @@ execute({command} [, {silent}]) *execute()*
|
|||||||
Note: Text attributes (highlights) are not captured.
|
Note: Text attributes (highlights) are not captured.
|
||||||
|
|
||||||
exepath({expr}) *exepath()*
|
exepath({expr}) *exepath()*
|
||||||
If {expr} is an executable and is either an absolute path, a
|
Returns the full path of {expr} if it is an executable and
|
||||||
relative path or found in $PATH, return the full path.
|
given as a (partial or full) path or is found in $PATH.
|
||||||
Note that the current directory is used when {expr} starts
|
Returns empty string otherwise.
|
||||||
with "./", which may be a problem for Vim: >
|
If {expr} starts with "./" the |current-directory| is used.
|
||||||
echo exepath(v:progpath)
|
|
||||||
< If {expr} cannot be found in $PATH or is not executable then
|
|
||||||
an empty string is returned.
|
|
||||||
|
|
||||||
*exists()*
|
*exists()*
|
||||||
exists({expr}) The result is a Number, which is non-zero if {expr} is
|
exists({expr}) The result is a Number, which is non-zero if {expr} is
|
||||||
|
@@ -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>
|
<C-Tab>, <C-S-Tab>, <C-BS>, <C-S-BS>, <C-Enter>, <C-S-Enter>
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
'inccommand' shows results while typing a |:substitute| command
|
'inccommand' shows interactive results for |:substitute|-like commands
|
||||||
'statusline' supports unlimited alignment sections
|
'statusline' supports unlimited alignment sections
|
||||||
'tabline' %@Func@foo%X can call any function on mouse-click
|
'tabline' %@Func@foo%X can call any function on mouse-click
|
||||||
|
|
||||||
Variables:
|
Variables:
|
||||||
|v:event|
|
|v:event|
|
||||||
|
|v:progpath| is always absolute ("full")
|
||||||
|v:windowid| is always available (for use by external UIs)
|
|v:windowid| is always available (for use by external UIs)
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
|
@@ -238,9 +238,7 @@ int main(int argc, char **argv)
|
|||||||
// Check if we have an interactive window.
|
// Check if we have an interactive window.
|
||||||
check_and_set_isatty(¶ms);
|
check_and_set_isatty(¶ms);
|
||||||
|
|
||||||
// Get the name with which Nvim was invoked, with and without path.
|
init_path(argv[0]);
|
||||||
set_vim_var_string(VV_PROGPATH, argv[0], -1);
|
|
||||||
set_vim_var_string(VV_PROGNAME, (char *) path_tail((char_u *) argv[0]), -1);
|
|
||||||
|
|
||||||
event_init();
|
event_init();
|
||||||
/*
|
/*
|
||||||
@@ -1194,9 +1192,27 @@ static void check_and_set_isatty(mparm_T *paramp)
|
|||||||
paramp->err_isatty = os_isatty(fileno(stderr));
|
paramp->err_isatty = os_isatty(fileno(stderr));
|
||||||
TIME_MSG("window checked");
|
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)
|
static char_u *get_fname(mparm_T *parmp, char_u *cwd)
|
||||||
{
|
{
|
||||||
#if !defined(UNIX)
|
#if !defined(UNIX)
|
||||||
|
@@ -831,3 +831,43 @@ char_u *get_env_name(expand_T *xp, int idx)
|
|||||||
return NULL;
|
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;
|
||||||
|
}
|
||||||
|
@@ -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)
|
if (!use_path || path_is_absolute_path(name)
|
||||||
|| (name[0] == '.'
|
|| (name[0] == '.'
|
||||||
&& (name[1] == '/' || (name[1] == '.' && name[2] == '/')))) {
|
&& (name[1] == '/' || (name[1] == '.' && name[2] == '/')))) {
|
||||||
#if WIN32
|
#ifdef WIN32
|
||||||
bool ok = is_executable(name);
|
bool ok = is_executable(name);
|
||||||
#else
|
#else
|
||||||
// Must have path separator, cannot execute files in the current directory.
|
// 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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if WIN32
|
#ifdef WIN32
|
||||||
// Windows does not have exec bit; just check if the file exists and is not
|
// Windows does not have exec bit; just check if the file exists and is not
|
||||||
// a directory.
|
// a directory.
|
||||||
return (S_ISREG(mode));
|
return (S_ISREG(mode));
|
||||||
@@ -281,7 +281,7 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath)
|
|||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
// Prepend ".;" to $PATH.
|
// Prepend ".;" to $PATH.
|
||||||
size_t pathlen = strlen(path_env);
|
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);
|
memcpy(path + 2, path_env, pathlen);
|
||||||
#else
|
#else
|
||||||
char *path = xstrdup(path_env);
|
char *path = xstrdup(path_env);
|
||||||
@@ -1027,7 +1027,7 @@ shortcut_end:
|
|||||||
int os_translate_sys_error(int sys_errno) {
|
int os_translate_sys_error(int sys_errno) {
|
||||||
#ifdef HAVE_UV_TRANSLATE_SYS_ERROR
|
#ifdef HAVE_UV_TRANSLATE_SYS_ERROR
|
||||||
return uv_translate_sys_error(sys_errno);
|
return uv_translate_sys_error(sys_errno);
|
||||||
#elif WIN32
|
#elif defined(WIN32)
|
||||||
// TODO(equalsraf): libuv does not yet expose uv_translate_sys_error()
|
// TODO(equalsraf): libuv does not yet expose uv_translate_sys_error()
|
||||||
// in its public API, include a version here until it can be used.
|
// in its public API, include a version here until it can be used.
|
||||||
// See https://github.com/libuv/libuv/issues/79
|
// See https://github.com/libuv/libuv/issues/79
|
||||||
|
@@ -16,7 +16,8 @@
|
|||||||
// Special wildcards that need to be handled by the shell.
|
// Special wildcards that need to be handled by the shell.
|
||||||
#define SPECIAL_WILDCHAR "`'{"
|
#define SPECIAL_WILDCHAR "`'{"
|
||||||
|
|
||||||
// Separator character for environment variables.
|
// Character that separates entries in $PATH.
|
||||||
#define ENV_SEPCHAR ':'
|
#define ENV_SEPCHAR ':'
|
||||||
|
#define ENV_SEPSTR ":"
|
||||||
|
|
||||||
#endif // NVIM_OS_UNIX_DEFS_H
|
#endif // NVIM_OS_UNIX_DEFS_H
|
||||||
|
@@ -20,8 +20,9 @@
|
|||||||
|
|
||||||
#define FNAME_ILLEGAL "\"*?><|"
|
#define FNAME_ILLEGAL "\"*?><|"
|
||||||
|
|
||||||
// Separator character for environment variables.
|
// Character that separates entries in $PATH.
|
||||||
#define ENV_SEPCHAR ';'
|
#define ENV_SEPCHAR ';'
|
||||||
|
#define ENV_SEPSTR ";"
|
||||||
|
|
||||||
#define USE_CRNL
|
#define USE_CRNL
|
||||||
|
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
local helpers = require('test.functional.helpers')(after_each)
|
local helpers = require('test.functional.helpers')(after_each)
|
||||||
local eq, clear, execute, call, iswin, write_file =
|
local eq, clear, call, iswin, write_file =
|
||||||
helpers.eq, helpers.clear, helpers.execute, helpers.call, helpers.iswin,
|
helpers.eq, helpers.clear, helpers.call, helpers.iswin, helpers.write_file
|
||||||
helpers.write_file
|
|
||||||
|
|
||||||
describe('executable()', function()
|
describe('executable()', function()
|
||||||
before_each(clear)
|
before_each(clear)
|
||||||
@@ -15,8 +14,28 @@ describe('executable()', function()
|
|||||||
eq(0, call('executable', 'no_such_file_exists_209ufq23f'))
|
eq(0, call('executable', 'no_such_file_exists_209ufq23f'))
|
||||||
end)
|
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()
|
describe('exec-bit', function()
|
||||||
setup(function()
|
setup(function()
|
||||||
|
clear()
|
||||||
write_file('Xtest_not_executable', 'non-executable file')
|
write_file('Xtest_not_executable', 'non-executable file')
|
||||||
write_file('Xtest_executable', 'executable file (exec-bit set)')
|
write_file('Xtest_executable', 'executable file (exec-bit set)')
|
||||||
if not iswin() then -- N/A for Windows.
|
if not iswin() then -- N/A for Windows.
|
||||||
|
@@ -96,6 +96,10 @@ describe('startup defaults', function()
|
|||||||
eq(meths.get_option('runtimepath'), meths.get_option('packpath'))
|
eq(meths.get_option('runtimepath'), meths.get_option('packpath'))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('v:progpath is set to the absolute path', function()
|
||||||
|
eq(eval("fnamemodify(v:progpath, ':p')"), eval('v:progpath'))
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe('XDG-based defaults', function()
|
describe('XDG-based defaults', function()
|
||||||
|
@@ -14,15 +14,15 @@ local cimp = cimport('./src/nvim/os/os.h')
|
|||||||
|
|
||||||
describe('env function', function()
|
describe('env function', function()
|
||||||
local function os_setenv(name, value, override)
|
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
|
end
|
||||||
|
|
||||||
local function os_unsetenv(name, _, _)
|
local function os_unsetenv(name, _, _)
|
||||||
return cimp.os_unsetenv((to_cstr(name)))
|
return cimp.os_unsetenv(to_cstr(name))
|
||||||
end
|
end
|
||||||
|
|
||||||
local function os_getenv(name)
|
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
|
if rval ~= NULL then
|
||||||
return ffi.string(rval)
|
return ffi.string(rval)
|
||||||
else
|
else
|
||||||
@@ -52,6 +52,20 @@ describe('env function', function()
|
|||||||
end)
|
end)
|
||||||
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()
|
describe('os_getenv', function()
|
||||||
it('reads an env variable', function()
|
it('reads an env variable', function()
|
||||||
local name = 'NEOVIM_UNIT_TEST_GETENV_1N'
|
local name = 'NEOVIM_UNIT_TEST_GETENV_1N'
|
||||||
|
Reference in New Issue
Block a user