diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2017-02-02 13:16:15 +0100 |
---|---|---|
committer | Justin M. Keyes <justinkz@gmail.com> | 2017-02-04 11:07:49 +0100 |
commit | 224f99b85d311ebd31451db13b66e4a3c7e51938 (patch) | |
tree | de2bb4bc8f06e09ede603ae535697384133a4ed5 | |
parent | 7d58aba80c6d81a9af40f54e566c0cdcea2de3e3 (diff) | |
download | rneovim-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.txt | 17 | ||||
-rw-r--r-- | runtime/doc/vim_diff.txt | 3 | ||||
-rw-r--r-- | src/nvim/main.c | 28 | ||||
-rw-r--r-- | src/nvim/os/env.c | 40 | ||||
-rw-r--r-- | src/nvim/os/fs.c | 8 | ||||
-rw-r--r-- | src/nvim/os/unix_defs.h | 3 | ||||
-rw-r--r-- | src/nvim/os/win_defs.h | 3 | ||||
-rw-r--r-- | test/functional/eval/executable_spec.lua | 25 | ||||
-rw-r--r-- | test/functional/options/defaults_spec.lua | 4 | ||||
-rw-r--r-- | test/unit/os/env_spec.lua | 20 |
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(¶ms); - // 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' |