aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin M. Keyes <justinkz@gmail.com>2017-05-13 18:17:21 +0200
committerJustin M. Keyes <justinkz@gmail.com>2017-05-15 15:01:52 +0200
commit4c5398bc402357468ccb4dfc07d6867a44c18a23 (patch)
tree7d0f79e720bcdf0ec1a0f69d0bebf3e203790c54
parent6e4e70f51b8a889d38fe5d954d9ac817750424c3 (diff)
downloadrneovim-4c5398bc402357468ccb4dfc07d6867a44c18a23.tar.gz
rneovim-4c5398bc402357468ccb4dfc07d6867a44c18a23.tar.bz2
rneovim-4c5398bc402357468ccb4dfc07d6867a44c18a23.zip
startup: v:progpath fallback: path_guess_exepath
If procfs is missing then libuv cannot find the exe path. Fallback to path_guess_exepath(), adapted from Vim findYourself(). Closes #6734
-rw-r--r--runtime/doc/eval.txt4
-rw-r--r--src/nvim/main.c3
-rw-r--r--src/nvim/os/fs.c6
-rw-r--r--src/nvim/path.c47
-rw-r--r--test/unit/os/env_spec.lua2
-rw-r--r--test/unit/path_spec.lua55
6 files changed, 109 insertions, 8 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 873200cb30..44f2b3688c 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -1749,9 +1749,7 @@ v:profiling Normally zero. Set to one after using ":profile start".
See |profiling|.
*v:progname* *progname-variable*
-v:progname Contains the name (with path removed) with which Nvim was
- invoked. Allows you to do special initialisations for any
- name you might symlink to Nvim.
+v:progname The name by which Nvim was invoked (with path removed).
Read-only.
*v:progpath* *progpath-variable*
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 40b553e93c..c4c7ae8559 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -1223,7 +1223,8 @@ static void init_path(char *exename)
size_t exepathlen = MAXPATHL;
// Make v:progpath absolute.
if (os_exepath(exepath, &exepathlen) != 0) {
- EMSG2(e_intern2, "init_path()");
+ // Fall back to argv[0]. Missing procfs? #6734
+ path_guess_exepath(exename, exepath, sizeof(exepath));
}
set_vim_var_string(VV_PROGPATH, exepath, -1);
set_vim_var_string(VV_PROGNAME, (char *)path_tail((char_u *)exename), -1);
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index aaa750db50..b9a9480cb8 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -196,11 +196,13 @@ int os_nodetype(const char *name)
}
/// Gets the absolute path of the currently running executable.
+/// May fail if procfs is missing. #6734
+/// @see path_exepath
///
-/// @param[out] buffer Returns the path string.
+/// @param[out] buffer Full path to the executable.
/// @param[in] size Size of `buffer`.
///
-/// @return `0` on success, or libuv error code on failure.
+/// @return 0 on success, or libuv error code.
int os_exepath(char *buffer, size_t *size)
FUNC_ATTR_NONNULL_ALL
{
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 12952f49db..caf1ce20d6 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -2237,3 +2237,50 @@ int path_is_absolute_path(const char_u *fname)
return *fname == '/' || *fname == '~';
#endif
}
+
+/// Builds a full path from an invocation name `argv0`, based on heuristics.
+///
+/// @param[in] argv0 Name by which Nvim was invoked.
+/// @param[out] buf Guessed full path to `argv0`.
+/// @param[in] bufsize Size of `buf`.
+///
+/// @see os_exepath
+void path_guess_exepath(const char *argv0, char *buf, size_t bufsize)
+ FUNC_ATTR_NONNULL_ALL
+{
+ char *path = getenv("PATH");
+
+ if (path == NULL || path_is_absolute_path((char_u *)argv0)) {
+ xstrlcpy(buf, argv0, bufsize);
+ } else if (argv0[0] == '.' || strchr(argv0, PATHSEP)) {
+ // Relative to CWD.
+ if (os_dirname((char_u *)buf, MAXPATHL) != OK) {
+ buf[0] = NUL;
+ }
+ xstrlcat(buf, PATHSEPSTR, bufsize);
+ xstrlcat(buf, argv0, bufsize);
+ } else {
+ // Search $PATH for plausible location.
+ const void *iter = NULL;
+ do {
+ const char *dir;
+ size_t dir_len;
+ iter = vim_colon_env_iter(path, iter, &dir, &dir_len);
+ if (dir == NULL || dir_len == 0) {
+ break;
+ }
+ if (dir_len + 1 > sizeof(NameBuff)) {
+ continue;
+ }
+ xstrlcpy((char *)NameBuff, dir, dir_len + 1);
+ xstrlcat((char *)NameBuff, PATHSEPSTR, sizeof(NameBuff));
+ xstrlcat((char *)NameBuff, argv0, sizeof(NameBuff));
+ if (os_can_exe(NameBuff, NULL, false)) {
+ xstrlcpy(buf, (char *)NameBuff, bufsize);
+ return;
+ }
+ } while (iter != NULL);
+ // Not found in $PATH, fall back to argv0.
+ xstrlcpy(buf, argv0, bufsize);
+ }
+}
diff --git a/test/unit/os/env_spec.lua b/test/unit/os/env_spec.lua
index 575787a25e..cefd0315b7 100644
--- a/test/unit/os/env_spec.lua
+++ b/test/unit/os/env_spec.lua
@@ -13,7 +13,7 @@ require('lfs')
local cimp = cimport('./src/nvim/os/os.h')
-describe('env function', function()
+describe('env.c', function()
local function os_setenv(name, value, override)
return cimp.os_setenv(to_cstr(name), to_cstr(value), override)
end
diff --git a/test/unit/path_spec.lua b/test/unit/path_spec.lua
index 6b9e2c8695..a9cba7df84 100644
--- a/test/unit/path_spec.lua
+++ b/test/unit/path_spec.lua
@@ -18,7 +18,7 @@ local cimp = cimport('./src/nvim/os/os.h', './src/nvim/path.h')
local length = 0
local buffer = nil
-describe('path function', function()
+describe('path.c', function()
describe('path_full_dir_name', function()
setup(function()
lfs.mkdir('unit-test-directory')
@@ -293,6 +293,59 @@ describe('path_shorten_fname_if_possible', function()
end)
end)
+describe('path.c path_guess_exepath', function()
+ local cwd = lfs.currentdir()
+
+ for _,name in ipairs({'./nvim', '.nvim', 'foo/nvim'}) do
+ itp('"'..name..'" returns name catenated with CWD', function()
+ local bufsize = 255
+ local buf = cstr(bufsize, '')
+ cimp.path_guess_exepath(name, buf, bufsize)
+ eq(cwd..'/'..name, ffi.string(buf))
+ end)
+ end
+
+ itp('absolute path returns the name unmodified', function()
+ local name = '/foo/bar/baz'
+ local bufsize = 255
+ local buf = cstr(bufsize, '')
+ cimp.path_guess_exepath(name, buf, bufsize)
+ eq(name, ffi.string(buf))
+ end)
+
+ itp('returns the name unmodified if not found in $PATH', function()
+ local name = '23u0293_not_in_path'
+ local bufsize = 255
+ local buf = cstr(bufsize, '')
+ cimp.path_guess_exepath(name, buf, bufsize)
+ eq(name, ffi.string(buf))
+ end)
+
+ itp('does not crash if $PATH item exceeds MAXPATHL', function()
+ local orig_path_env = os.getenv('PATH')
+ local name = 'cat' -- Some executable in $PATH.
+ local bufsize = 255
+ local buf = cstr(bufsize, '')
+ local insane_path = orig_path_env..':'..(("x/"):rep(4097))
+
+ cimp.os_setenv('PATH', insane_path, true)
+ cimp.path_guess_exepath(name, buf, bufsize)
+ eq('bin/' .. name, ffi.string(buf):sub(-#('bin/' .. name), -1))
+
+ -- Restore $PATH.
+ cimp.os_setenv('PATH', orig_path_env, true)
+ end)
+
+ itp('returns full path found in $PATH', function()
+ local name = 'cat' -- Some executable in $PATH.
+ local bufsize = 255
+ local buf = cstr(bufsize, '')
+ cimp.path_guess_exepath(name, buf, bufsize)
+ -- Usually "/bin/cat" on unix, "/path/to/nvim/cat" on Windows.
+ eq('bin/' .. name, ffi.string(buf):sub(-#('bin/' .. name), -1))
+ end)
+end)
+
describe('path.c', function()
setup(function()
lfs.mkdir('unit-test-directory');