diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2017-05-13 18:17:21 +0200 |
---|---|---|
committer | Justin M. Keyes <justinkz@gmail.com> | 2017-05-15 15:01:52 +0200 |
commit | 4c5398bc402357468ccb4dfc07d6867a44c18a23 (patch) | |
tree | 7d0f79e720bcdf0ec1a0f69d0bebf3e203790c54 | |
parent | 6e4e70f51b8a889d38fe5d954d9ac817750424c3 (diff) | |
download | rneovim-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.txt | 4 | ||||
-rw-r--r-- | src/nvim/main.c | 3 | ||||
-rw-r--r-- | src/nvim/os/fs.c | 6 | ||||
-rw-r--r-- | src/nvim/path.c | 47 | ||||
-rw-r--r-- | test/unit/os/env_spec.lua | 2 | ||||
-rw-r--r-- | test/unit/path_spec.lua | 55 |
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'); |