diff options
-rw-r--r-- | src/nvim/os/fs.c | 70 |
1 files changed, 67 insertions, 3 deletions
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 8f2324c554..0fbd1b0b34 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -258,8 +258,9 @@ bool os_can_exe(const char_u *name, char_u **abspath, bool use_path) if (!pathext) { pathext = ".com;.exe;.bat;.cmd"; } - bool ok = is_executable((char *)name) || is_executable_ext((char *)name, - pathext); + bool ok = (is_extension_executable((char *)name) + && is_executable((char *)name)) + || is_executable_ext((char *)name, pathext); #else // Must have path separator, cannot execute files in the current directory. const bool ok = ((const char_u *)gettail_dir((const char *)name) != name @@ -277,6 +278,68 @@ bool os_can_exe(const char_u *name, char_u **abspath, bool use_path) return is_executable_in_path(name, abspath); } +#ifdef WIN32 +/// Returns true if extension of `name` is executalbe file exteinsion. +static bool is_extension_executable(const char *name) + FUNC_ATTR_NONNULL_ALL +{ + // Don't check extensions, when a Unix-shell like 'shell'. + const char_u *shell_end = p_sh + STRLEN(p_sh); + while (true) { + if (*shell_end == '.') { + break; + } else if (shell_end == p_sh + || (*shell_end == '/' || *shell_end == '\\')) { + shell_end = p_sh + STRLEN(p_sh); + break; + } + shell_end--; + } + if (mb_strnicmp(shell_end - 2, (const char_u *)"sh", 2) == 0) { + return true; + } + + const char *pathext = os_getenv("PATHEXT"); + if (!pathext) { + pathext = ".com;.exe;.bat;.cmd"; + } + const char *ext_pos = name + STRLEN(name) - 1; + while (name != ext_pos) { + if (*ext_pos == '\\' || *ext_pos == '/') { + ext_pos = name; + break; + } + if (*ext_pos == '.') { + break; + } + ext_pos--; + } + + const char *cur_pos = pathext; + while (true) { + // Don't check extension, if $PATHEXT contain dot itself. + if (*cur_pos == '.' + && (*(cur_pos + 1) == ENV_SEPCHAR || *(cur_pos + 1) == NUL)) { + return true; + } + const char *ext_end = strchr(cur_pos, ENV_SEPCHAR); + size_t ext_len = ext_end ? + (size_t)(ext_end - cur_pos) : + (STRLEN(pathext) - (size_t)(cur_pos - pathext)); + if (ext_pos != name && mb_strnicmp((const char_u *)ext_pos, + (const char_u *)cur_pos, ext_len) == 0) { + return true; + } + if (ext_end == NULL) { + break; + } else { + cur_pos = ++ext_end; + } + } + return false; +} +#endif + /// Returns true if `name` is an executable file. static bool is_executable(const char *name) FUNC_ATTR_NONNULL_ALL @@ -378,7 +441,8 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath) append_path(buf, (char *)name, buf_len); #ifdef WIN32 - bool ok = is_executable(buf) || is_executable_ext(buf, pathext); + bool ok = (is_extension_executable(buf) && is_executable(buf)) + || is_executable_ext(buf, pathext); #else bool ok = is_executable(buf); #endif |