diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2017-02-03 16:56:31 +0100 |
---|---|---|
committer | Justin M. Keyes <justinkz@gmail.com> | 2017-02-04 11:07:50 +0100 |
commit | 67fbbdb1b5eb6e48c7c533042abba9e409833ca6 (patch) | |
tree | e99ae56f4f3140564e91d7b136018b61103105be | |
parent | cd5b1315757ed3c66aa63c6df69582503c5b81dd (diff) | |
download | rneovim-67fbbdb1b5eb6e48c7c533042abba9e409833ca6.tar.gz rneovim-67fbbdb1b5eb6e48c7c533042abba9e409833ca6.tar.bz2 rneovim-67fbbdb1b5eb6e48c7c533042abba9e409833ca6.zip |
win: executable(): full path without extension
Absolute path is considered executable even *without* an extension.
-rw-r--r-- | src/nvim/os/fs.c | 111 | ||||
-rw-r--r-- | src/nvim/path.c | 25 | ||||
-rw-r--r-- | test/functional/eval/executable_spec.lua | 26 |
3 files changed, 89 insertions, 73 deletions
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 4ca67d1f1a..7266e54b58 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -219,37 +219,42 @@ int os_exepath(char *buffer, size_t *size) bool os_can_exe(const char_u *name, char_u **abspath, bool use_path) FUNC_ATTR_NONNULL_ARG(1) { - // when use_path is false or if it's an absolute or relative path don't - // need to use $PATH. - if (!use_path || path_is_absolute_path(name) - || (name[0] == '.' - && (name[1] == '/' || (name[1] == '.' && name[2] == '/')))) { + bool no_path = !use_path || path_is_absolute_path(name); +#ifndef WIN32 + // If the filename is "qualified" (relative or absolute) do not check $PATH. + no_path |= (name[0] == '.' + && (name[1] == '/' || (name[1] == '.' && name[2] == '/'))); +#endif + + if (no_path) { #ifdef WIN32 - bool ok = is_executable(name); + const char *pathext = os_getenv("PATHEXT"); + if (!pathext) { + pathext = ".com;.exe;.bat;.cmd"; + } + bool ok = is_executable((char *)name) || is_executable_ext((char *)name, + pathext); #else // Must have path separator, cannot execute files in the current directory. - bool ok = gettail_dir(name) != name && is_executable(name); + bool ok = gettail_dir(name) != name && is_executable((char *)name); #endif if (ok) { if (abspath != NULL) { *abspath = save_absolute_path(name); } - return true; } - return false; } return is_executable_in_path(name, abspath); } -// Return true if "name" is an executable file, false if not or it doesn't -// exist. -static bool is_executable(const char_u *name) +/// Returns true if `name` is an executable file. +static bool is_executable(const char *name) FUNC_ATTR_NONNULL_ALL { - int32_t mode = os_getperm(name); + int32_t mode = os_getperm((char_u *)name); if (mode < 0) { return false; @@ -264,6 +269,37 @@ static bool is_executable(const char_u *name) #endif } +#ifdef WIN32 +/// Appends file extensions from `pathext` to `name` and returns true if any +/// such combination is executable. +static bool is_executable_ext(char *name, const char *pathext) + FUNC_ATTR_NONNULL_ALL +{ + xstrlcpy((char *)NameBuff, name, sizeof(NameBuff)); + char *buf_end = xstrchrnul((char *)NameBuff, '\0'); + for (const char *ext = pathext; *ext; ext++) { + // Skip the extension if there is no suffix after a '.'. + if (ext[0] == '.' && (ext[1] == '\0' || ext[1] == ENV_SEPCHAR)) { + ext++; + continue; + } + + const char *ext_end = xstrchrnul(ext, ENV_SEPCHAR); + STRLCPY(buf_end, ext, ext_end - ext + 1); + + if (is_executable((char *)NameBuff)) { + return true; + } + + if (*ext_end != ENV_SEPCHAR) { + break; + } + ext = ext_end; + } + return false; +} +#endif + /// Checks if a file is inside the `$PATH` and is executable. /// /// @param[in] name The name of the executable. @@ -294,11 +330,10 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath) if (!pathext) { pathext = ".com;.exe;.bat;.cmd"; } - buf_len += strlen(pathext); #endif - char_u *buf = xmalloc(buf_len); + char *buf = xmalloc(buf_len); // Walk through all entries in $PATH to check if "name" exists there and // is an executable file. @@ -307,50 +342,24 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath) for (;; ) { char *e = xstrchrnul(p, ENV_SEPCHAR); - // Glue the directory from $PATH with `name` and save into buf. + // Combine the $PATH segment with `name`. STRLCPY(buf, p, e - p + 1); - append_path((char *)buf, (char *)name, buf_len); + append_path(buf, (char *)name, buf_len); - if (is_executable(buf)) { - // Check if the caller asked for a copy of the path. - if (abspath != NULL) { - *abspath = save_absolute_path(buf); +#ifdef WIN32 + bool ok = is_executable(buf) || is_executable_ext(buf, pathext); +#else + bool ok = is_executable(buf); +#endif + if (ok) { + if (abspath != NULL) { // Caller asked for a copy of the path. + *abspath = save_absolute_path((char_u *)buf); } rv = true; goto end; } -#ifdef WIN32 - // Try appending file extensions from $PATHEXT to the name. - char *buf_end = xstrchrnul((char *)buf, '\0'); - for (const char *ext = pathext; *ext; ext++) { - // Skip the extension if there is no suffix after a '.'. - if (ext[0] == '.' && (ext[1] == '\0' || ext[1] == ENV_SEPCHAR)) { - *ext++; - continue; - } - - const char *ext_end = xstrchrnul(ext, ENV_SEPCHAR); - STRLCPY(buf_end, ext, ext_end - ext + 1); - - if (is_executable(buf)) { - // Check if the caller asked for a copy of the path. - if (abspath != NULL) { - *abspath = save_absolute_path(buf); - } - - rv = true; - goto end; - } - - if (*ext_end != ENV_SEPCHAR) { - break; - } - ext = ext_end; - } -#endif - if (*e != ENV_SEPCHAR) { // End of $PATH without finding any executable called name. goto end; diff --git a/src/nvim/path.c b/src/nvim/path.c index 374d72ddd3..ea06fb8dde 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -995,12 +995,10 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern) ga_remove_duplicate_strings(gap); } -/* - * Return the end of the directory name, on the first path - * separator: - * "/path/file", "/path/dir/", "/path//dir", "/file" - * ^ ^ ^ ^ - */ +/// Return the end of the directory name, on the first path +/// separator: +/// "/path/file", "/path/dir/", "/path//dir", "/file" +/// ^ ^ ^ ^ char_u *gettail_dir(const char_u *fname) { const char_u *dir_end = fname; @@ -2131,17 +2129,12 @@ int append_path(char *path, const char *to_append, size_t max_len) size_t current_length = strlen(path); size_t to_append_length = strlen(to_append); - // Do not append empty strings. - if (to_append_length == 0) { - return OK; - } - - // Do not append a dot. - if (STRCMP(to_append, ".") == 0) { + // Do not append empty string or a dot. + if (to_append_length == 0 || strcmp(to_append, ".") == 0) { return OK; } - // Glue both paths with a slash. + // Combine the path segments, separated by a slash. if (current_length > 0 && !vim_ispathsep_nocolon(path[current_length-1])) { current_length += 1; // Count the trailing slash. @@ -2150,7 +2143,7 @@ int append_path(char *path, const char *to_append, size_t max_len) return FAIL; } - STRCAT(path, PATHSEPSTR); + xstrlcat(path, PATHSEPSTR, max_len); } // +1 for the NUL at the end. @@ -2158,7 +2151,7 @@ int append_path(char *path, const char *to_append, size_t max_len) return FAIL; } - STRCAT(path, to_append); + xstrlcat(path, to_append, max_len); return OK; } diff --git a/test/functional/eval/executable_spec.lua b/test/functional/eval/executable_spec.lua index 13b9261d82..7948ddaa40 100644 --- a/test/functional/eval/executable_spec.lua +++ b/test/functional/eval/executable_spec.lua @@ -20,14 +20,8 @@ describe('executable()', function() -- 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') - print(helpers.eval("echo glob(fnamemodify(exepath(v:progpath), ':h').'/*')")) eq('arg1=lemon;arg2=sky;arg3=tree;', call('system', sibling_exe..' lemon sky tree')) end @@ -103,6 +97,26 @@ describe('executable() (Windows)', function() eq(0, call('executable', '.\\test_executable_zzz')) end) + it('full path with extension', function() + -- Some executable we can expect in the test env. + local exe = 'printargs-test' + local exedir = helpers.eval("fnamemodify(v:progpath, ':h')") + local exepath = exedir..'/'..exe..'.exe' + eq(1, call('executable', exepath)) + eq('arg1=lemon;arg2=sky;arg3=tree;', + call('system', exepath..' lemon sky tree')) + end) + + it('full path without extension', function() + -- Some executable we can expect in the test env. + local exe = 'printargs-test' + local exedir = helpers.eval("fnamemodify(v:progpath, ':h')") + local exepath = exedir..'/'..exe + eq('arg1=lemon;arg2=sky;arg3=tree;', + call('system', exepath..' lemon sky tree')) + eq(1, call('executable', exepath)) + end) + it('respects $PATHEXT when trying extensions on a filename', function() clear({env={PATHEXT='.zzz'}}) for _,ext in ipairs(exts) do |