mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
commit
bb2f36d038
@ -1,6 +1,3 @@
|
||||
get_filename_component(BUSTED_DIR ${BUSTED_PRG} PATH)
|
||||
set(ENV{PATH} "${BUSTED_DIR}:$ENV{PATH}")
|
||||
|
||||
set(ENV{VIMRUNTIME} ${WORKING_DIR}/runtime)
|
||||
set(ENV{NVIM_RPLUGIN_MANIFEST} ${WORKING_DIR}/Xtest_rplugin_manifest)
|
||||
set(ENV{XDG_CONFIG_HOME} ${WORKING_DIR}/Xtest_xdg/config)
|
||||
|
@ -280,7 +280,7 @@ function! s:check_python(version) abort
|
||||
let python_bin = exepath(python_bin_name)
|
||||
|
||||
if exists('$PATH')
|
||||
for path in split($PATH, ':')
|
||||
for path in split($PATH, has('win32') ? ';' : ':')
|
||||
let path_bin = path.'/'.python_bin_name
|
||||
if path_bin != python_bin && index(python_multiple, path_bin) == -1
|
||||
\ && executable(path_bin)
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -4086,10 +4086,8 @@ void fname_expand(buf_T *buf, char_u **ffname, char_u **sfname)
|
||||
|
||||
#ifdef WIN32
|
||||
if (!buf->b_p_bin) {
|
||||
char_u *rfname;
|
||||
|
||||
// If the file name is a shortcut file, use the file it links to.
|
||||
rfname = os_resolve_shortcut(*ffname);
|
||||
char_u *rfname = (char_u *)os_resolve_shortcut(*ffname);
|
||||
if (rfname != NULL) {
|
||||
xfree(*ffname);
|
||||
*ffname = rfname;
|
||||
|
@ -14137,11 +14137,9 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||
p = get_tv_string(&argvars[0]);
|
||||
#ifdef WIN32
|
||||
{
|
||||
char_u *v = NULL;
|
||||
|
||||
v = os_resolve_shortcut(p);
|
||||
char *v = os_resolve_shortcut(p);
|
||||
if (v != NULL) {
|
||||
rettv->vval.v_string = v;
|
||||
rettv->vval.v_string = (char_u *)v;
|
||||
} else {
|
||||
rettv->vval.v_string = vim_strsave(p);
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -1517,7 +1517,7 @@ int utf16_to_utf8(const WCHAR *strw, char **str)
|
||||
0,
|
||||
strw,
|
||||
-1,
|
||||
(LPSTR *)pos,
|
||||
pos,
|
||||
utf8_len,
|
||||
NULL,
|
||||
NULL);
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "nvim/vim.h"
|
||||
#include "nvim/ascii.h"
|
||||
#include "nvim/charset.h"
|
||||
#include "nvim/fileio.h"
|
||||
#include "nvim/os/os.h"
|
||||
#include "nvim/memory.h"
|
||||
#include "nvim/message.h"
|
||||
@ -18,6 +19,10 @@
|
||||
#include "nvim/ex_getln.h"
|
||||
#include "nvim/version.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#include "nvim/mbyte.h" // for utf8_to_utf16, utf16_to_utf8
|
||||
#endif
|
||||
|
||||
#ifdef HAVE__NSGETENVIRON
|
||||
#include <crt_externs.h>
|
||||
#endif
|
||||
@ -45,7 +50,21 @@ bool os_env_exists(const char *name)
|
||||
int os_setenv(const char *name, const char *value, int overwrite)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
#ifdef HAVE_SETENV
|
||||
#ifdef WIN32
|
||||
size_t envbuflen = strlen(name) + strlen(value) + 2;
|
||||
char *envbuf = xmalloc(envbuflen);
|
||||
snprintf(envbuf, envbuflen, "%s=%s", name, value);
|
||||
|
||||
WCHAR *p;
|
||||
utf8_to_utf16(envbuf, &p);
|
||||
xfree(envbuf);
|
||||
if (p == NULL) {
|
||||
return -1;
|
||||
}
|
||||
_wputenv(p);
|
||||
xfree(p); // Unlike Unix systems, we can free the string for _wputenv().
|
||||
return 0;
|
||||
#elif defined(HAVE_SETENV)
|
||||
return setenv(name, value, overwrite);
|
||||
#elif defined(HAVE_PUTENV_S)
|
||||
if (!overwrite && os_getenv(name) != NULL) {
|
||||
@ -831,3 +850,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;
|
||||
}
|
||||
|
172
src/nvim/os/fs.c
172
src/nvim/os/fs.c
@ -219,49 +219,86 @@ 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] == '/')))) {
|
||||
// There must be a path separator, files in the current directory
|
||||
// can't be executed
|
||||
if (gettail_dir(name) != name && is_executable(name)) {
|
||||
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
|
||||
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((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;
|
||||
}
|
||||
|
||||
#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));
|
||||
#else
|
||||
return (S_ISREG(mode) && (S_IXUSR & mode));
|
||||
#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.
|
||||
///
|
||||
@ -272,89 +309,69 @@ static bool is_executable(const char_u *name)
|
||||
static bool is_executable_in_path(const char_u *name, char_u **abspath)
|
||||
FUNC_ATTR_NONNULL_ARG(1)
|
||||
{
|
||||
const char *path = os_getenv("PATH");
|
||||
if (path == NULL) {
|
||||
const char *path_env = os_getenv("PATH");
|
||||
if (path_env == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t buf_len = STRLEN(name) + STRLEN(path) + 2;
|
||||
#ifdef WIN32
|
||||
// Prepend ".;" to $PATH.
|
||||
size_t pathlen = strlen(path_env);
|
||||
char *path = memcpy(xmallocz(pathlen + 3), "." ENV_SEPSTR, 2);
|
||||
memcpy(path + 2, path_env, pathlen);
|
||||
#else
|
||||
char *path = xstrdup(path_env);
|
||||
#endif
|
||||
|
||||
size_t buf_len = STRLEN(name) + strlen(path) + 2;
|
||||
|
||||
#ifdef WIN32
|
||||
const char *pathext = os_getenv("PATHEXT");
|
||||
if (!pathext) {
|
||||
pathext = ".com;.exe;.bat;.cmd";
|
||||
}
|
||||
|
||||
buf_len += STRLEN(pathext);
|
||||
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.
|
||||
char *p = path;
|
||||
bool rv = false;
|
||||
for (;; ) {
|
||||
const char *e = xstrchrnul(path, ENV_SEPCHAR);
|
||||
char *e = xstrchrnul(p, ENV_SEPCHAR);
|
||||
|
||||
// Glue together the given directory from $PATH with name and save into
|
||||
// buf.
|
||||
STRLCPY(buf, path, e - path + 1);
|
||||
append_path((char *) buf, (const 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);
|
||||
}
|
||||
|
||||
xfree(buf);
|
||||
|
||||
return true;
|
||||
}
|
||||
// Combine the $PATH segment with `name`.
|
||||
STRLCPY(buf, p, e - p + 1);
|
||||
append_path(buf, (char *)name, buf_len);
|
||||
|
||||
#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] == ';')) {
|
||||
*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);
|
||||
}
|
||||
|
||||
xfree(buf);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (*ext_end != ENV_SEPCHAR) {
|
||||
break;
|
||||
}
|
||||
ext = ext_end;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
if (*e != ENV_SEPCHAR) {
|
||||
// End of $PATH without finding any executable called name.
|
||||
xfree(buf);
|
||||
return false;
|
||||
goto end;
|
||||
}
|
||||
|
||||
path = e + 1;
|
||||
p = e + 1;
|
||||
}
|
||||
|
||||
// We should never get to this point.
|
||||
assert(false);
|
||||
return false;
|
||||
end:
|
||||
xfree(buf);
|
||||
xfree(path);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/// Opens or creates a file and returns a non-negative integer representing
|
||||
@ -407,11 +424,11 @@ ptrdiff_t os_read(const int fd, bool *ret_eof, char *const ret_buf,
|
||||
size_t read_bytes = 0;
|
||||
bool did_try_to_free = false;
|
||||
while (read_bytes != size) {
|
||||
assert(size >= read_bytes);
|
||||
const ptrdiff_t cur_read_bytes = read(fd, ret_buf + read_bytes,
|
||||
size - read_bytes);
|
||||
if (cur_read_bytes > 0) {
|
||||
read_bytes += (size_t)cur_read_bytes;
|
||||
assert(read_bytes <= size);
|
||||
}
|
||||
if (cur_read_bytes < 0) {
|
||||
const int error = os_translate_sys_error(errno);
|
||||
@ -510,6 +527,7 @@ ptrdiff_t os_write(const int fd, const char *const buf, const size_t size)
|
||||
}
|
||||
size_t written_bytes = 0;
|
||||
while (written_bytes != size) {
|
||||
assert(size >= written_bytes);
|
||||
const ptrdiff_t cur_written_bytes = write(fd, buf + written_bytes,
|
||||
size - written_bytes);
|
||||
if (cur_written_bytes > 0) {
|
||||
@ -932,12 +950,12 @@ bool os_fileid_equal_fileinfo(const FileID *file_id,
|
||||
/// When "fname" is the name of a shortcut (*.lnk) resolve the file it points
|
||||
/// to and return that name in allocated memory.
|
||||
/// Otherwise NULL is returned.
|
||||
char_u * os_resolve_shortcut(char_u *fname)
|
||||
char *os_resolve_shortcut(char_u *fname)
|
||||
{
|
||||
HRESULT hr;
|
||||
IPersistFile *ppf = NULL;
|
||||
OLECHAR wsz[MAX_PATH];
|
||||
char_u *rfname = NULL;
|
||||
char *rfname = NULL;
|
||||
int len;
|
||||
IShellLinkW *pslw = NULL;
|
||||
WIN32_FIND_DATAW ffdw;
|
||||
@ -1019,7 +1037,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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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) {
|
||||
// Do not append empty string or a dot.
|
||||
if (to_append_length == 0 || strcmp(to_append, ".") == 0) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
// Do not append a dot.
|
||||
if (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;
|
||||
}
|
||||
|
||||
|
156
test/functional/eval/executable_spec.lua
Normal file
156
test/functional/eval/executable_spec.lua
Normal file
@ -0,0 +1,156 @@
|
||||
local helpers = require('test.functional.helpers')(after_each)
|
||||
local eq, clear, call, iswin, write_file =
|
||||
helpers.eq, helpers.clear, helpers.call, helpers.iswin, helpers.write_file
|
||||
|
||||
describe('executable()', function()
|
||||
before_each(clear)
|
||||
|
||||
it('returns 1 for commands in $PATH', function()
|
||||
local exe = iswin() and 'ping' or 'ls'
|
||||
eq(1, call('executable', exe))
|
||||
end)
|
||||
|
||||
it('returns 0 for non-existent files', 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
|
||||
-- $PATH on AppVeyor CI might be oversized, redefine it to a minimal one.
|
||||
clear({env={PATH=[[C:\Windows\system32;C:\Windows]]}})
|
||||
eq('arg1=lemon;arg2=sky;arg3=tree;',
|
||||
call('system', sibling_exe..' lemon sky tree'))
|
||||
end
|
||||
local is_executable = call('executable', sibling_exe)
|
||||
if iswin() and is_executable ~= expected then
|
||||
pending('XXX: sometimes fails on AppVeyor')
|
||||
end
|
||||
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.
|
||||
call('system', {'chmod', '-x', 'Xtest_not_executable'})
|
||||
call('system', {'chmod', '+x', 'Xtest_executable'})
|
||||
end
|
||||
end)
|
||||
|
||||
teardown(function()
|
||||
os.remove('Xtest_not_executable')
|
||||
os.remove('Xtest_executable')
|
||||
end)
|
||||
|
||||
it('not set', function()
|
||||
local expected = iswin() and 1 or 0
|
||||
eq(expected, call('executable', 'Xtest_not_executable'))
|
||||
eq(expected, call('executable', './Xtest_not_executable'))
|
||||
end)
|
||||
|
||||
it('set, unqualified and not in $PATH', function()
|
||||
local expected = iswin() and 1 or 0
|
||||
eq(expected, call('executable', 'Xtest_executable'))
|
||||
end)
|
||||
|
||||
it('set, qualified as a path', function()
|
||||
eq(1, call('executable', './Xtest_executable'))
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('executable() (Windows)', function()
|
||||
if not iswin() then return end -- N/A for Unix.
|
||||
|
||||
local exts = {'bat', 'exe', 'com', 'cmd'}
|
||||
setup(function()
|
||||
for _, ext in ipairs(exts) do
|
||||
write_file('test_executable_'..ext..'.'..ext, '')
|
||||
end
|
||||
write_file('test_executable_zzz.zzz', '')
|
||||
end)
|
||||
|
||||
teardown(function()
|
||||
for _, ext in ipairs(exts) do
|
||||
os.remove('test_executable_'..ext..'.'..ext)
|
||||
end
|
||||
os.remove('test_executable_zzz.zzz')
|
||||
end)
|
||||
|
||||
it('tries default extensions on a filename if $PATHEXT is empty', function()
|
||||
-- Empty $PATHEXT defaults to ".com;.exe;.bat;.cmd".
|
||||
clear({env={PATHEXT=''}})
|
||||
for _,ext in ipairs(exts) do
|
||||
eq(1, call('executable', 'test_executable_'..ext))
|
||||
end
|
||||
eq(0, call('executable', 'test_executable_zzz'))
|
||||
end)
|
||||
|
||||
it('tries default extensions on a filepath if $PATHEXT is empty', function()
|
||||
-- Empty $PATHEXT defaults to ".com;.exe;.bat;.cmd".
|
||||
clear({env={PATHEXT=''}})
|
||||
for _,ext in ipairs(exts) do
|
||||
eq(1, call('executable', '.\\test_executable_'..ext))
|
||||
end
|
||||
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
|
||||
eq(0, call('executable', 'test_executable_'..ext))
|
||||
end
|
||||
eq(1, call('executable', 'test_executable_zzz'))
|
||||
end)
|
||||
|
||||
it('respects $PATHEXT when trying extensions on a filepath', function()
|
||||
clear({env={PATHEXT='.zzz'}})
|
||||
for _,ext in ipairs(exts) do
|
||||
eq(0, call('executable', '.\\test_executable_'..ext))
|
||||
end
|
||||
eq(1, call('executable', '.\\test_executable_zzz'))
|
||||
end)
|
||||
|
||||
it('returns 1 for any existing filename', function()
|
||||
clear({env={PATHEXT=''}})
|
||||
for _,ext in ipairs(exts) do
|
||||
eq(1, call('executable', 'test_executable_'..ext..'.'..ext))
|
||||
end
|
||||
eq(1, call('executable', 'test_executable_zzz.zzz'))
|
||||
end)
|
||||
|
||||
it('returns 1 for any existing path (backslashes)', function()
|
||||
clear({env={PATHEXT=''}})
|
||||
for _,ext in ipairs(exts) do
|
||||
eq(1, call('executable', '.\\test_executable_'..ext..'.'..ext))
|
||||
eq(1, call('executable', './test_executable_'..ext..'.'..ext))
|
||||
end
|
||||
eq(1, call('executable', '.\\test_executable_zzz.zzz'))
|
||||
eq(1, call('executable', './test_executable_zzz.zzz'))
|
||||
end)
|
||||
end)
|
@ -290,7 +290,9 @@ local function clear(...)
|
||||
'NVIM_LOG_FILE',
|
||||
'NVIM_RPLUGIN_MANIFEST',
|
||||
}) do
|
||||
env_tbl[k] = os.getenv(k)
|
||||
if not env_tbl[k] then
|
||||
env_tbl[k] = os.getenv(k)
|
||||
end
|
||||
end
|
||||
env = {}
|
||||
for k, v in pairs(env_tbl) do
|
||||
|
@ -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()
|
||||
|
@ -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'
|
||||
|
@ -23,7 +23,7 @@ cimport('./src/nvim/os/shell.h')
|
||||
cimport('./src/nvim/option_defs.h')
|
||||
cimport('./src/nvim/main.h')
|
||||
cimport('./src/nvim/fileio.h')
|
||||
local fs = cimport('./src/nvim/os/os.h')
|
||||
local fs = cimport('./src/nvim/os/os.h', './src/nvim/path.h')
|
||||
cppimport('sys/stat.h')
|
||||
cppimport('fcntl.h')
|
||||
cppimport('uv-errno.h')
|
||||
@ -77,11 +77,9 @@ describe('fs function', function()
|
||||
lfs.link('test.file', 'unit-test-directory/test_link.file', true)
|
||||
|
||||
lfs.link('non_existing_file.file', 'unit-test-directory/test_broken_link.file', true)
|
||||
-- Since the tests are executed, they are called by an executable. We use
|
||||
-- that executable for several asserts.
|
||||
-- The tests are invoked with an absolute path to `busted` executable.
|
||||
absolute_executable = arg[0]
|
||||
-- Split absolute_executable into a directory and the actual file name for
|
||||
-- later usage.
|
||||
-- Split the absolute_executable path into a directory and filename.
|
||||
directory, executable_name = string.match(absolute_executable, '^(.*)/(.*)$')
|
||||
end)
|
||||
|
||||
@ -194,11 +192,8 @@ describe('fs function', function()
|
||||
end)
|
||||
|
||||
it('returns the absolute path when given an executable inside $PATH', function()
|
||||
-- Since executable_name does not start with "./", the path will be
|
||||
-- selected from $PATH. Make sure the ends match, ignore the directories.
|
||||
local _, busted = string.match(absolute_executable, '^(.*)/(.*)$')
|
||||
local _, name = string.match(exe(executable_name), '^(.*)/(.*)$')
|
||||
eq(busted, name)
|
||||
local fullpath = exe('ls')
|
||||
eq(1, fs.path_is_absolute_path(to_cstr(fullpath)))
|
||||
end)
|
||||
|
||||
it('returns the absolute path when given an executable relative to the current dir', function()
|
||||
|
Loading…
Reference in New Issue
Block a user