aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/os
diff options
context:
space:
mode:
authorJustin M. Keyes <justinkz@gmail.com>2017-02-05 01:11:06 +0100
committerGitHub <noreply@github.com>2017-02-05 01:11:06 +0100
commitbb2f36d038ccb375249a078997f68840ddcc66ef (patch)
treea080d374a6b38607806ca553d00b2d3e8b590c36 /src/nvim/os
parent3d3b1641d0cae3244c09d4602ab96d290dd0928a (diff)
parent18127f64c421a2c4da100a9e40d49c31a9a5170a (diff)
downloadrneovim-bb2f36d038ccb375249a078997f68840ddcc66ef.tar.gz
rneovim-bb2f36d038ccb375249a078997f68840ddcc66ef.tar.bz2
rneovim-bb2f36d038ccb375249a078997f68840ddcc66ef.zip
Merge #6038 from justinmk/win32-executable
win: executable()
Diffstat (limited to 'src/nvim/os')
-rw-r--r--src/nvim/os/env.c61
-rw-r--r--src/nvim/os/fs.c168
-rw-r--r--src/nvim/os/unix_defs.h3
-rw-r--r--src/nvim/os/win_defs.h3
4 files changed, 157 insertions, 78 deletions
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index 747a34d8ce..5a3c1ef2c8 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -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;
+}
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 30e08ac129..097c672887 100644
--- a/src/nvim/os/fs.c
+++ b/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;
+ 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);
}
- if (*ext_end != ENV_SEPCHAR) {
- break;
- }
- ext = ext_end;
+ rv = true;
+ goto end;
}
-#endif
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
diff --git a/src/nvim/os/unix_defs.h b/src/nvim/os/unix_defs.h
index 690a39c3cd..c98aa88bfa 100644
--- a/src/nvim/os/unix_defs.h
+++ b/src/nvim/os/unix_defs.h
@@ -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
diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h
index 6a29f86e79..8de896c490 100644
--- a/src/nvim/os/win_defs.h
+++ b/src/nvim/os/win_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