aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/os
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/os')
-rw-r--r--src/nvim/os/dl.c2
-rw-r--r--src/nvim/os/env.c183
-rw-r--r--src/nvim/os/fs.c197
-rw-r--r--src/nvim/os/fs_defs.h10
-rw-r--r--src/nvim/os/input.c30
-rw-r--r--src/nvim/os/mem.c2
-rw-r--r--src/nvim/os/os_defs.h68
-rw-r--r--src/nvim/os/shell.c28
-rw-r--r--src/nvim/os/signal.c12
-rw-r--r--src/nvim/os/stdpaths.c23
-rw-r--r--src/nvim/os/time.c4
-rw-r--r--src/nvim/os/unix_defs.h28
-rw-r--r--src/nvim/os/users.c6
-rw-r--r--src/nvim/os/win_defs.h68
14 files changed, 445 insertions, 216 deletions
diff --git a/src/nvim/os/dl.c b/src/nvim/os/dl.c
index b4a35e203e..fef02cc784 100644
--- a/src/nvim/os/dl.c
+++ b/src/nvim/os/dl.c
@@ -63,7 +63,7 @@ bool os_libcall(const char *libname,
// call the library and save the result
// TODO(aktau): catch signals and use jmp (if available) to handle
- // exceptions. jmp's on UNIX seem to interact trickily with signals as
+ // exceptions. jmp's on Unix seem to interact trickily with signals as
// well. So for now we only support those libraries that are well-behaved.
if (str_out) {
str_str_fn sfn = (str_str_fn) fn;
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index bf6db97fcf..edc430410c 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -46,7 +46,19 @@ 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
return setenv(name, value, overwrite);
+#elif defined(HAVE_PUTENV_S)
+ if (!overwrite && os_getenv(name) != NULL) {
+ return 0;
+ }
+ if (_putenv_s(name, value) == 0) {
+ return 0;
+ }
+ return -1;
+#else
+# error "This system has no implementation available for os_setenv()"
+#endif
}
/// Unset environment variable
@@ -130,29 +142,50 @@ void os_get_hostname(char *hostname, size_t len)
/// - go to that directory
/// - do os_dirname() to get the real name of that directory.
/// This also works with mounts and links.
-/// Don't do this for MS-DOS, it will change the "current dir" for a drive.
+/// Don't do this for Windows, it will change the "current dir" for a drive.
static char_u *homedir = NULL;
void init_homedir(void)
{
- /* In case we are called a second time (when 'encoding' changes). */
+ // In case we are called a second time (when 'encoding' changes).
xfree(homedir);
homedir = NULL;
char_u *var = (char_u *)os_getenv("HOME");
+#ifdef WIN32
+ // Typically, $HOME is not defined on Windows, unless the user has
+ // specifically defined it for Vim's sake. However, on Windows NT
+ // platforms, $HOMEDRIVE and $HOMEPATH are automatically defined for
+ // each user. Try constructing $HOME from these.
+ if (var == NULL) {
+ const char *homedrive = os_getenv("HOMEDRIVE");
+ const char *homepath = os_getenv("HOMEPATH");
+ if (homepath == NULL) {
+ homepath = "\\";
+ }
+ if (homedrive != NULL && strlen(homedrive) + strlen(homepath) < MAXPATHL) {
+ snprintf((char *)NameBuff, MAXPATHL, "%s%s", homedrive, homepath);
+ if (NameBuff[0] != NUL) {
+ var = NameBuff;
+ vim_setenv("HOME", (char *)NameBuff);
+ }
+ }
+ }
+#endif
+
if (var != NULL) {
#ifdef UNIX
- /*
- * Change to the directory and get the actual path. This resolves
- * links. Don't do it when we can't return.
- */
+ // Change to the directory and get the actual path. This resolves
+ // links. Don't do it when we can't return.
if (os_dirname(NameBuff, MAXPATHL) == OK
&& os_chdir((char *)NameBuff) == 0) {
- if (!os_chdir((char *)var) && os_dirname(IObuff, IOSIZE) == OK)
+ if (!os_chdir((char *)var) && os_dirname(IObuff, IOSIZE) == OK) {
var = IObuff;
- if (os_chdir((char *)NameBuff) != 0)
+ }
+ if (os_chdir((char *)NameBuff) != 0) {
EMSG(_(e_prev_dir));
+ }
}
#endif
homedir = vim_strsave(var);
@@ -206,31 +239,49 @@ void expand_env(char_u *src, char_u *dst, int dstlen)
/// "~/" is also expanded, using $HOME. For Unix "~user/" is expanded.
/// Skips over "\ ", "\~" and "\$" (not for Win32 though).
/// If anything fails no expansion is done and dst equals src.
-/// startstr recognize the start of a new name, for '~' expansion.
+/// prefix recognize the start of a new name, for '~' expansion.
/// @param srcp Input string e.g. "$HOME/vim.hlp"
/// @param dst Where to put the result
/// @param dstlen Maximum length of the result
/// @param esc Should we escape spaces in expanded variables?
/// @param one Should we expand more than one '~'?
-/// @param startstr Common prefix for paths, can be NULL
-void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one,
- char_u *startstr)
+/// @param prefix Common prefix for paths, can be NULL
+void expand_env_esc(char_u *restrict srcp,
+ char_u *restrict dst,
+ int dstlen,
+ bool esc,
+ bool one,
+ char_u *prefix)
{
- char_u *src;
char_u *tail;
- int c;
char_u *var;
bool copy_char;
bool mustfree; // var was allocated, need to free it later
bool at_start = true; // at start of a name
- int startstr_len = 0;
- if (startstr != NULL)
- startstr_len = (int)STRLEN(startstr);
+ int prefix_len = (prefix == NULL) ? 0 : (int)STRLEN(prefix);
- src = skipwhite(srcp);
- --dstlen; // leave one char space for "\,"
+ char_u *src = skipwhite(srcp);
+ dstlen--; // leave one char space for "\,"
while (*src && dstlen > 0) {
+ // Skip over `=expr`.
+ if (src[0] == '`' && src[1] == '=') {
+ var = src;
+ src += 2;
+ (void)skip_expr(&src);
+ if (*src == '`') {
+ src++;
+ }
+ size_t len = (size_t)(src - var);
+ if (len > (size_t)dstlen) {
+ len = (size_t)dstlen;
+ }
+ memcpy((char *)dst, (char *)var, len);
+ dst += len;
+ dstlen -= (int)len;
+ continue;
+ }
+
copy_char = true;
if ((*src == '$') || (*src == '~' && at_start)) {
mustfree = false;
@@ -240,14 +291,15 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one,
if (*src != '~') { // environment var
tail = src + 1;
var = dst;
- c = dstlen - 1;
+ int c = dstlen - 1;
#ifdef UNIX
// Unix has ${var-name} type environment vars
if (*tail == '{' && !vim_isIDc('{')) {
- tail++; /* ignore '{' */
- while (c-- > 0 && *tail && *tail != '}')
+ tail++; // ignore '{'
+ while (c-- > 0 && *tail != NUL && *tail != '}') {
*var++ = *tail++;
+ }
} else // NOLINT
#endif
{
@@ -257,7 +309,7 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one,
}
#if defined(UNIX)
- // Verify that we have found the end of a UNIX ${VAR} style variable
+ // Verify that we have found the end of a Unix ${VAR} style variable
if (src[1] == '{' && *tail != '}') {
var = NULL;
} else {
@@ -271,7 +323,7 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one,
#if defined(UNIX)
}
#endif
- } else if ( src[1] == NUL /* home directory */
+ } else if (src[1] == NUL // home directory
|| vim_ispathsep(src[1])
|| vim_strchr((char_u *)" ,\t\n", src[1]) != NULL) {
var = homedir;
@@ -281,12 +333,13 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one,
// Copy ~user to dst[], so we can put a NUL after it.
tail = src;
var = dst;
- c = dstlen - 1;
- while ( c-- > 0
- && *tail
- && vim_isfilec(*tail)
- && !vim_ispathsep(*tail))
+ int c = dstlen - 1;
+ while (c-- > 0
+ && *tail
+ && vim_isfilec(*tail)
+ && !vim_ispathsep(*tail)) {
*var++ = *tail++;
+ }
*var = NUL;
// Use os_get_user_directory() to get the user directory.
// If this function fails, the shell is used to
@@ -294,8 +347,7 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one,
// does not support ~user (old versions of /bin/sh).
var = (char_u *)os_get_user_directory((char *)dst + 1);
mustfree = true;
- if (var == NULL)
- {
+ if (var == NULL) {
expand_T xpc;
ExpandInit(&xpc);
@@ -331,8 +383,9 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one,
if (esc && var != NULL && vim_strpbrk(var, (char_u *)" \t") != NULL) {
char_u *p = vim_strsave_escaped(var, (char_u *)" \t");
- if (mustfree)
+ if (mustfree) {
xfree(var);
+ }
var = p;
mustfree = true;
}
@@ -341,7 +394,7 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one,
&& (STRLEN(var) + STRLEN(tail) + 1 < (unsigned)dstlen)) {
STRCPY(dst, var);
dstlen -= (int)STRLEN(var);
- c = (int)STRLEN(var);
+ int c = (int)STRLEN(var);
// if var[] ends in a path separator and tail[] starts
// with it, skip a character
if (*var != NUL && after_pathsep((char *)dst, (char *)dst + c)
@@ -354,8 +407,9 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one,
src = tail;
copy_char = false;
}
- if (mustfree)
+ if (mustfree) {
xfree(var);
+ }
}
if (copy_char) { // copy at least one char
@@ -372,9 +426,10 @@ void expand_env_esc(char_u *srcp, char_u *dst, int dstlen, bool esc, bool one,
*dst++ = *src++;
--dstlen;
- if (startstr != NULL && src - startstr_len >= srcp
- && STRNCMP(src - startstr_len, startstr, startstr_len) == 0)
+ if (prefix != NULL && src - prefix_len >= srcp
+ && STRNCMP(src - prefix_len, prefix, prefix_len) == 0) {
at_start = true;
+ }
}
}
*dst = NUL;
@@ -401,17 +456,37 @@ static char *vim_version_dir(const char *vimdir)
return NULL;
}
-/// If the string between "p" and "pend" ends in "name/", return "pend" minus
-/// the length of "name/". Otherwise return "pend".
-static char *remove_tail(char *p, char *pend, char *name)
+/// If `dirname + "/"` precedes `pend` in the path, return the pointer to
+/// `dirname + "/" + pend`. Otherwise return `pend`.
+///
+/// Examples (path = /usr/local/share/nvim/runtime/doc/help.txt):
+///
+/// pend = help.txt
+/// dirname = doc
+/// -> doc/help.txt
+///
+/// pend = doc/help.txt
+/// dirname = runtime
+/// -> runtime/doc/help.txt
+///
+/// pend = runtime/doc/help.txt
+/// dirname = vim74
+/// -> runtime/doc/help.txt
+///
+/// @param path Path to a file
+/// @param pend A suffix of the path
+/// @param dirname The immediate path fragment before the pend
+/// @return The new pend including dirname or just pend
+static char *remove_tail(char *path, char *pend, char *dirname)
{
- size_t len = STRLEN(name) + 1;
- char *newend = pend - len;
+ size_t len = STRLEN(dirname);
+ char *new_tail = pend - len - 1;
- if (newend >= p
- && fnamencmp((char_u *)newend, (char_u *)name, len - 1) == 0
- && (newend == p || after_pathsep(p, newend)))
- return newend;
+ if (new_tail >= path
+ && fnamencmp((char_u *)new_tail, (char_u *)dirname, len) == 0
+ && (new_tail == path || after_pathsep(path, new_tail))) {
+ return new_tail;
+ }
return pend;
}
@@ -695,9 +770,10 @@ void home_replace(buf_T *buf, char_u *src, char_u *dst, int dstlen, bool one)
/// @param src Input file name
char_u * home_replace_save(buf_T *buf, char_u *src) FUNC_ATTR_NONNULL_RET
{
- size_t len = 3; /* space for "~/" and trailing NUL */
- if (src != NULL) /* just in case */
+ size_t len = 3; // space for "~/" and trailing NUL
+ if (src != NULL) { // just in case
len += STRLEN(src);
+ }
char_u *dst = xmalloc(len);
home_replace(buf, src, dst, (int)len, true);
return dst;
@@ -709,15 +785,15 @@ char_u * home_replace_save(buf_T *buf, char_u *src) FUNC_ATTR_NONNULL_RET
void vim_setenv(const char *name, const char *val)
{
os_setenv(name, val, 1);
- /*
- * When setting $VIMRUNTIME adjust the directory to find message
- * translations to $VIMRUNTIME/lang.
- */
+#ifndef LOCALE_INSTALL_DIR
+ // When setting $VIMRUNTIME adjust the directory to find message
+ // translations to $VIMRUNTIME/lang.
if (*val != NUL && STRICMP(name, "VIMRUNTIME") == 0) {
char *buf = (char *)concat_str((char_u *)val, (char_u *)"/lang");
- bindtextdomain(VIMPACKAGE, buf);
+ bindtextdomain(PROJECT_NAME, buf);
xfree(buf);
}
+#endif
}
@@ -733,8 +809,7 @@ char_u *get_env_name(expand_T *xp, int idx)
STRLCPY(name, envname, ENVNAMELEN);
xfree(envname);
return name;
- } else {
- return NULL;
}
+ return NULL;
}
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 05f0f53c63..49a74cf0d1 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -2,6 +2,7 @@
#include <stdbool.h>
#include <assert.h>
+#include <fcntl.h>
#include "nvim/os/os.h"
#include "nvim/os/os_defs.h"
@@ -59,6 +60,23 @@ int os_dirname(char_u *buf, size_t len)
return OK;
}
+/// Check if the given path is a directory and not a symlink to a directory.
+/// @return `true` if `name` is a directory and NOT a symlink to a directory.
+/// `false` if `name` is not a directory or if an error occurred.
+bool os_isrealdir(const char_u *name)
+ FUNC_ATTR_NONNULL_ALL
+{
+ uv_fs_t request;
+ if (uv_fs_lstat(&fs_loop, &request, (char *)name, NULL) != kLibuvSuccess) {
+ return false;
+ }
+ if (S_ISLNK(request.statbuf.st_mode)) {
+ return false;
+ } else {
+ return S_ISDIR(request.statbuf.st_mode);
+ }
+}
+
/// Check if the given path is a directory or not.
///
/// @return `true` if `fname` is a directory.
@@ -77,10 +95,76 @@ bool os_isdir(const char_u *name)
return true;
}
+/// Check what `name` is:
+/// @return NODE_NORMAL: file or directory (or doesn't exist)
+/// NODE_WRITABLE: writable device, socket, fifo, etc.
+/// NODE_OTHER: non-writable things
+int os_nodetype(const char *name)
+{
+#ifdef WIN32
+ // Edge case from Vim os_win32.c:
+ // We can't open a file with a name "\\.\con" or "\\.\prn", trying to read
+ // from it later will cause Vim to hang. Thus return NODE_WRITABLE here.
+ if (STRNCMP(name, "\\\\.\\", 4) == 0) {
+ return NODE_WRITABLE;
+ }
+#endif
+
+ uv_stat_t statbuf;
+ if (os_stat(name, &statbuf) == 0) {
+ return NODE_NORMAL;
+ }
+
+#ifndef WIN32
+ // libuv does not handle BLK and DIR in uv_handle_type.
+ // Related: https://github.com/joyent/libuv/pull/1421
+ if (S_ISREG(statbuf.st_mode) || S_ISDIR(statbuf.st_mode)) {
+ return NODE_NORMAL;
+ }
+ if (S_ISBLK(statbuf.st_mode)) { // block device isn't writable
+ return NODE_OTHER;
+ }
+#endif
+
+ // Vim os_win32.c:mch_nodetype does this (since patch 7.4.015):
+ // if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) {
+ // wn = enc_to_utf16(name, NULL);
+ // hFile = CreatFile(wn, ...)
+ // to get a HANDLE. But libuv just calls win32's _get_osfhandle() on the fd we
+ // give it. uv_fs_open calls fs__capture_path which does a similar dance and
+ // saves us the hassle.
+
+ int nodetype = NODE_WRITABLE;
+ int fd = os_open(name, O_RDONLY, 0);
+ switch(uv_guess_handle(fd)) {
+ case UV_TTY: // FILE_TYPE_CHAR
+ nodetype = NODE_WRITABLE;
+ break;
+ case UV_FILE: // FILE_TYPE_DISK
+ nodetype = NODE_NORMAL;
+ break;
+ case UV_NAMED_PIPE: // not handled explicitly in Vim os_win32.c
+ case UV_UDP: // unix only
+ case UV_TCP: // unix only
+ case UV_UNKNOWN_HANDLE:
+ default:
+#ifdef WIN32
+ nodetype = NODE_NORMAL;
+#else
+ nodetype = NODE_WRITABLE; // Everything else is writable?
+#endif
+ break;
+ }
+
+ close(fd);
+ return nodetype;
+}
+
/// Checks if the given path represents an executable file.
///
-/// @param[in] name The name of the executable.
+/// @param[in] name Name of the executable.
/// @param[out] abspath Path of the executable, if found and not `NULL`.
+/// @param[in] use_path If 'false', only check if "name" is executable
///
/// @return `true` if `name` is executable and
/// - can be found in $PATH,
@@ -88,14 +172,18 @@ bool os_isdir(const char_u *name)
/// - is absolute.
///
/// @return `false` otherwise.
-bool os_can_exe(const char_u *name, char_u **abspath)
+bool os_can_exe(const char_u *name, char_u **abspath, bool use_path)
FUNC_ATTR_NONNULL_ARG(1)
{
- // If it's an absolute or relative path don't need to use $PATH.
- if (path_is_absolute_path(name) ||
- (name[0] == '.' && (name[1] == '/' ||
- (name[1] == '.' && name[2] == '/')))) {
- if (is_executable(name)) {
+ // 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)) {
if (abspath != NULL) {
*abspath = save_absolute_path(name);
}
@@ -140,24 +228,33 @@ 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 = getenv("PATH");
- // PATH environment variable does not exist or is empty.
- if (path == NULL || *path == NUL) {
+ const char *path = os_getenv("PATH");
+ if (path == NULL) {
return false;
}
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);
+#endif
+
char_u *buf = xmalloc(buf_len);
// Walk through all entries in $PATH to check if "name" exists there and
// is an executable file.
for (;; ) {
- const char *e = xstrchrnul(path, ':');
+ const char *e = xstrchrnul(path, 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, (int)buf_len);
+ append_path((char *) buf, (const char *) name, buf_len);
if (is_executable(buf)) {
// Check if the caller asked for a copy of the path.
@@ -170,7 +267,39 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath)
return true;
}
- if (*e != ':') {
+#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;
+ }
+#endif
+
+ if (*e != ENV_SEPCHAR) {
// End of $PATH without finding any executable called name.
xfree(buf);
return false;
@@ -186,13 +315,13 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath)
/// Opens or creates a file and returns a non-negative integer representing
/// the lowest-numbered unused file descriptor, for use in subsequent system
-/// calls (read, write, lseek, fcntl, etc.). If the operation fails, `-errno`
-/// is returned, and no file is created or modified.
+/// calls (read, write, lseek, fcntl, etc.). If the operation fails, a libuv
+/// error code is returned, and no file is created or modified.
///
/// @param flags Bitwise OR of flags defined in <fcntl.h>
/// @param mode Permissions for the newly-created file (IGNORED if 'flags' is
/// not `O_CREAT` or `O_TMPFILE`), subject to the current umask
-/// @return file descriptor, or negative `errno` on failure
+/// @return file descriptor, or libuv error code on failure
int os_open(const char* path, int flags, int mode)
FUNC_ATTR_NONNULL_ALL
{
@@ -203,30 +332,44 @@ int os_open(const char* path, int flags, int mode)
return r;
}
+/// Flushes file modifications to disk.
+///
+/// @param fd the file descriptor of the file to flush to disk.
+///
+/// @return `0` on success, a libuv error code on failure.
+int os_fsync(int fd)
+{
+ uv_fs_t fsync_req;
+ int r = uv_fs_fsync(&fs_loop, &fsync_req, fd, NULL);
+ uv_fs_req_cleanup(&fsync_req);
+ return r;
+}
+
/// Get stat information for a file.
///
-/// @return OK on success, FAIL if a failure occurred.
-static bool os_stat(const char *name, uv_stat_t *statbuf)
+/// @return libuv return code.
+static int os_stat(const char *name, uv_stat_t *statbuf)
FUNC_ATTR_NONNULL_ALL
{
uv_fs_t request;
int result = uv_fs_stat(&fs_loop, &request, name, NULL);
*statbuf = request.statbuf;
uv_fs_req_cleanup(&request);
- return (result == kLibuvSuccess);
+ return result;
}
/// Get the file permissions for a given file.
///
-/// @return `-1` when `name` doesn't exist.
+/// @return libuv error code on error.
int32_t os_getperm(const char_u *name)
FUNC_ATTR_NONNULL_ALL
{
uv_stat_t statbuf;
- if (os_stat((char *)name, &statbuf)) {
+ int stat_result = os_stat((char *)name, &statbuf);
+ if (stat_result == kLibuvSuccess) {
return (int32_t)statbuf.st_mode;
} else {
- return -1;
+ return stat_result;
}
}
@@ -271,7 +414,7 @@ bool os_file_exists(const char_u *name)
FUNC_ATTR_NONNULL_ALL
{
uv_stat_t statbuf;
- return os_stat((char *)name, &statbuf);
+ return os_stat((char *)name, &statbuf) == kLibuvSuccess;
}
/// Check if a file is readable.
@@ -323,7 +466,7 @@ int os_rename(const char_u *path, const char_u *new_path)
/// Make a directory.
///
-/// @return `0` for success, -errno for failure.
+/// @return `0` for success, libuv error code for failure.
int os_mkdir(const char *path, int32_t mode)
FUNC_ATTR_NONNULL_ALL
{
@@ -343,7 +486,7 @@ int os_mkdir(const char *path, int32_t mode)
/// failed to create. I.e. it will contain dir or any
/// of the higher level directories.
///
-/// @return `0` for success, -errno for failure.
+/// @return `0` for success, libuv error code for failure.
int os_mkdir_recurse(const char *const dir, int32_t mode,
char **const failed_dir)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
@@ -471,7 +614,7 @@ int os_remove(const char *path)
bool os_fileinfo(const char *path, FileInfo *file_info)
FUNC_ATTR_NONNULL_ALL
{
- return os_stat(path, &(file_info->stat));
+ return os_stat(path, &(file_info->stat)) == kLibuvSuccess;
}
/// Get the file information for a given path without following links
@@ -573,7 +716,7 @@ bool os_fileid(const char *path, FileID *file_id)
FUNC_ATTR_NONNULL_ALL
{
uv_stat_t statbuf;
- if (os_stat(path, &statbuf)) {
+ if (os_stat(path, &statbuf) == kLibuvSuccess) {
file_id->inode = statbuf.st_ino;
file_id->device_id = statbuf.st_dev;
return true;
diff --git a/src/nvim/os/fs_defs.h b/src/nvim/os/fs_defs.h
index df1031b721..0bd9c37750 100644
--- a/src/nvim/os/fs_defs.h
+++ b/src/nvim/os/fs_defs.h
@@ -21,9 +21,15 @@ typedef struct {
uv_dirent_t ent; ///< @private The entry information.
} Directory;
-/// Function to convert -errno error to char * error description
+/// Function to convert libuv error to char * error description
///
-/// -errno errors are returned by a number of os functions.
+/// negative libuv error codes are returned by a number of os functions.
#define os_strerror uv_strerror
+// Values returned by os_nodetype()
+#define NODE_NORMAL 0 // file or directory, check with os_isdir()
+#define NODE_WRITABLE 1 // something we can write to (character
+ // device, fifo, socket, ..)
+#define NODE_OTHER 2 // non-writable thing (e.g., block device)
+
#endif // NVIM_OS_FS_DEFS_H
diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c
index df803609ae..7687b14f02 100644
--- a/src/nvim/os/input.c
+++ b/src/nvim/os/input.c
@@ -19,6 +19,7 @@
#include "nvim/getchar.h"
#include "nvim/main.h"
#include "nvim/misc1.h"
+#include "nvim/misc2.h"
#define READ_BUFFER_SIZE 0xfff
#define INPUT_BUFFER_SIZE (READ_BUFFER_SIZE * 4)
@@ -82,9 +83,11 @@ static void cursorhold_event(void **argv)
static void create_cursorhold_event(void)
{
- // If the queue had any items, this function should not have been
- // called(inbuf_poll would return kInputAvail)
- assert(queue_empty(loop.events));
+ // If events are enabled and the queue has any items, this function should not
+ // have been called(inbuf_poll would return kInputAvail)
+ // TODO(tarruda): Cursorhold should be implemented as a timer set during the
+ // `state_check` callback for the states where it can be triggered.
+ assert(!events_enabled || queue_empty(loop.events));
queue_put(loop.events, cursorhold_event, 0);
}
@@ -172,8 +175,9 @@ size_t input_enqueue(String keys)
char *ptr = keys.data, *end = ptr + keys.size;
while (rbuffer_space(input_buffer) >= 6 && ptr < end) {
- uint8_t buf[6] = {0};
- unsigned int new_size = trans_special((uint8_t **)&ptr, buf, true);
+ uint8_t buf[6] = { 0 };
+ unsigned int new_size = trans_special((const uint8_t **)&ptr, keys.size,
+ buf, true);
if (new_size) {
new_size = handle_mouse_event(&ptr, buf, new_size);
@@ -247,6 +251,14 @@ static unsigned int handle_mouse_event(char **ptr, uint8_t *buf,
int col, row, advance;
if (sscanf(*ptr, "<%d,%d>%n", &col, &row, &advance) != EOF && advance) {
if (col >= 0 && row >= 0) {
+ // Make sure the mouse position is valid. Some terminals may
+ // return weird values.
+ if (col >= Columns) {
+ col = (int)Columns - 1;
+ }
+ if (row >= Rows) {
+ row = (int)Rows - 1;
+ }
mouse_row = row;
mouse_col = col;
}
@@ -355,7 +367,7 @@ static void read_cb(Stream *stream, RBuffer *buf, size_t c, void *data,
static void process_interrupts(void)
{
- if (mapped_ctrl_c) {
+ if ((mapped_ctrl_c | curbuf->b_mapped_ctrl_c) & get_real_state()) {
return;
}
@@ -391,9 +403,9 @@ static int push_event_key(uint8_t *buf, int maxlen)
// Check if there's pending input
static bool input_ready(void)
{
- return typebuf_was_filled || // API call filled typeahead
- rbuffer_size(input_buffer) || // Input buffer filled
- pending_events(); // Events must be processed
+ return (typebuf_was_filled // API call filled typeahead
+ || rbuffer_size(input_buffer) // Input buffer filled
+ || pending_events()); // Events must be processed
}
// Exit because of an input read error.
diff --git a/src/nvim/os/mem.c b/src/nvim/os/mem.c
index 5e483c0c3d..871ece7a0e 100644
--- a/src/nvim/os/mem.c
+++ b/src/nvim/os/mem.c
@@ -8,5 +8,5 @@
uint64_t os_get_total_mem_kib(void)
{
// Convert bytes to KiB.
- return uv_get_total_memory() >> 10;
+ return uv_get_total_memory() / 1024;
}
diff --git a/src/nvim/os/os_defs.h b/src/nvim/os/os_defs.h
index 3d56115401..eee0cdd10b 100644
--- a/src/nvim/os/os_defs.h
+++ b/src/nvim/os/os_defs.h
@@ -13,24 +13,7 @@
# include "nvim/os/unix_defs.h"
#endif
-#if defined(DIRSIZ) && !defined(MAXNAMLEN)
-# define MAXNAMLEN DIRSIZ
-#endif
-
-#if defined(UFS_MAXNAMLEN) && !defined(MAXNAMLEN)
-# define MAXNAMLEN UFS_MAXNAMLEN /* for dynix/ptx */
-#endif
-
-#if defined(NAME_MAX) && !defined(MAXNAMLEN)
-# define MAXNAMLEN NAME_MAX /* for Linux before .99p3 */
-#endif
-
-// Default value.
-#ifndef MAXNAMLEN
-# define MAXNAMLEN 512
-#endif
-
-#define BASENAMELEN (MAXNAMLEN - 5)
+#define BASENAMELEN (NAME_MAX - 5)
// Use the system path length if it makes sense.
#if defined(PATH_MAX) && (PATH_MAX > 1000)
@@ -39,32 +22,6 @@
# define MAXPATHL 1024
#endif
-#ifndef FILETYPE_FILE
-# define FILETYPE_FILE "filetype.vim"
-#endif
-
-#ifndef FTPLUGIN_FILE
-# define FTPLUGIN_FILE "ftplugin.vim"
-#endif
-
-#ifndef INDENT_FILE
-# define INDENT_FILE "indent.vim"
-#endif
-
-#ifndef FTOFF_FILE
-# define FTOFF_FILE "ftoff.vim"
-#endif
-
-#ifndef FTPLUGOF_FILE
-# define FTPLUGOF_FILE "ftplugof.vim"
-#endif
-
-#ifndef INDOFF_FILE
-# define INDOFF_FILE "indoff.vim"
-#endif
-
-#define DFLT_ERRORFILE "errors.err"
-
// Command-processing buffer. Use large buffers for all platforms.
#define CMDBUFFSIZE 1024
@@ -77,25 +34,6 @@
# define DFLT_MAXMEMTOT (10*1024)
#endif
-#if !defined(S_ISDIR) && defined(S_IFDIR)
-# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
-#endif
-#if !defined(S_ISREG) && defined(S_IFREG)
-# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
-#endif
-#if !defined(S_ISBLK) && defined(S_IFBLK)
-# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
-#endif
-#if !defined(S_ISSOCK) && defined(S_IFSOCK)
-# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
-#endif
-#if !defined(S_ISFIFO) && defined(S_IFIFO)
-# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
-#endif
-#if !defined(S_ISCHR) && defined(S_IFCHR)
-# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
-#endif
-
// Note: Some systems need both string.h and strings.h (Savage). However,
// some systems can't handle both, only use string.h in that case.
#include <string.h>
@@ -103,9 +41,9 @@
# include <strings.h>
#endif
-/// Function to convert -errno error to char * error description
+/// Function to convert libuv error to char * error description
///
-/// -errno errors are returned by a number of os functions.
+/// negative libuv error codes are returned by a number of os functions.
#define os_strerror uv_strerror
#endif // NVIM_OS_OS_DEFS_H
diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c
index 57e25560de..f5a1637c94 100644
--- a/src/nvim/os/shell.c
+++ b/src/nvim/os/shell.c
@@ -36,7 +36,6 @@ typedef struct {
# include "os/shell.c.generated.h"
#endif
-
/// Builds the argument vector for running the user-configured 'shell' (p_sh)
/// with an optional command prefixed by 'shellcmdflag' (p_shcf).
///
@@ -45,9 +44,10 @@ typedef struct {
/// @return A newly allocated argument vector. It must be freed with
/// `shell_free_argv` when no longer needed.
char **shell_build_argv(const char *cmd, const char *extra_args)
+ FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC
{
- size_t argc = tokenize(p_sh, NULL) + tokenize(p_shcf, NULL);
- char **rv = xmalloc((unsigned)((argc + 4) * sizeof(char *)));
+ size_t argc = tokenize(p_sh, NULL) + (cmd ? tokenize(p_shcf, NULL) : 0);
+ char **rv = xmalloc((argc + 4) * sizeof(*rv));
// Split 'shell'
size_t i = tokenize(p_sh, rv);
@@ -338,24 +338,22 @@ static void out_data_cb(Stream *stream, RBuffer *buf, size_t count, void *data,
/// @param argv The vector that will be filled with copies of the parsed
/// words. It can be NULL if the caller only needs to count words.
/// @return The number of words parsed.
-static size_t tokenize(const char_u *str, char **argv)
+static size_t tokenize(const char_u *const str, char **const argv)
+ FUNC_ATTR_NONNULL_ARG(1)
{
- size_t argc = 0, len;
- char_u *p = (char_u *) str;
+ size_t argc = 0;
+ const char *p = (const char *) str;
while (*p != NUL) {
- len = word_length(p);
+ const size_t len = word_length((const char_u *) p);
if (argv != NULL) {
// Fill the slot
- argv[argc] = xmalloc(len + 1);
- memcpy(argv[argc], p, len);
- argv[argc][len] = NUL;
+ argv[argc] = vim_strnsave_unquoted(p, len);
}
argc++;
- p += len;
- p = skipwhite(p);
+ p = (const char *) skipwhite((char_u *) (p + len));
}
return argc;
@@ -377,6 +375,9 @@ static size_t word_length(const char_u *str)
if (*p == '"') {
// Found a quote character, switch the `inquote` flag
inquote = !inquote;
+ } else if (*p == '\\' && inquote) {
+ p++;
+ length++;
}
p++;
@@ -418,7 +419,8 @@ static void read_input(DynamicBuffer *buf)
// Finished a line, add a NL, unless this line should not have one.
// FIXME need to make this more readable
if (lnum != curbuf->b_op_end.lnum
- || !curbuf->b_p_bin
+ || (!curbuf->b_p_bin
+ && curbuf->b_p_fixeol)
|| (lnum != curbuf->b_no_eol_lnum
&& (lnum !=
curbuf->b_ml.ml_line_count
diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c
index 7158721433..0ff6016e32 100644
--- a/src/nvim/os/signal.c
+++ b/src/nvim/os/signal.c
@@ -32,9 +32,13 @@ void signal_init(void)
signal_watcher_init(&loop, &shup, NULL);
signal_watcher_init(&loop, &squit, NULL);
signal_watcher_init(&loop, &sterm, NULL);
+#ifdef SIGPIPE
signal_watcher_start(&spipe, on_signal, SIGPIPE);
+#endif
signal_watcher_start(&shup, on_signal, SIGHUP);
+#ifdef SIGQUIT
signal_watcher_start(&squit, on_signal, SIGQUIT);
+#endif
signal_watcher_start(&sterm, on_signal, SIGTERM);
#ifdef SIGPWR
signal_watcher_init(&loop, &spwr, NULL);
@@ -82,12 +86,16 @@ static char * signal_name(int signum)
case SIGPWR:
return "SIGPWR";
#endif
+#ifdef SIGPIPE
case SIGPIPE:
return "SIGPIPE";
+#endif
case SIGTERM:
return "SIGTERM";
+#ifdef SIGQUIT
case SIGQUIT:
return "SIGQUIT";
+#endif
case SIGHUP:
return "SIGHUP";
default:
@@ -123,11 +131,15 @@ static void on_signal(SignalWatcher *handle, int signum, void *data)
ml_sync_all(false, false);
break;
#endif
+#ifdef SIGPIPE
case SIGPIPE:
// Ignore
break;
+#endif
case SIGTERM:
+#ifdef SIGQUIT
case SIGQUIT:
+#endif
case SIGHUP:
if (!rejecting_deadly) {
deadly_signal(signum);
diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c
index ce374828f9..81ceb919c4 100644
--- a/src/nvim/os/stdpaths.c
+++ b/src/nvim/os/stdpaths.c
@@ -22,10 +22,10 @@ static const char *xdg_env_vars[] = {
static const char *const xdg_defaults[] = {
#ifdef WIN32
// Windows
- [kXDGConfigHome] = "$LOCALAPPDATA\\nvim\\config",
- [kXDGDataHome] = "$LOCALAPPDATA\\nvim\\data",
- [kXDGCacheHome] = "$LOCALAPPDATA\\nvim\\cache",
- [kXDGRuntimeDir] = "",
+ [kXDGConfigHome] = "$LOCALAPPDATA",
+ [kXDGDataHome] = "$LOCALAPPDATA",
+ [kXDGCacheHome] = "$TEMP",
+ [kXDGRuntimeDir] = NULL,
[kXDGConfigDirs] = NULL,
[kXDGDataDirs] = NULL,
#else
@@ -33,7 +33,7 @@ static const char *const xdg_defaults[] = {
[kXDGConfigHome] = "~/.config",
[kXDGDataHome] = "~/.local/share",
[kXDGCacheHome] = "~/.cache",
- [kXDGRuntimeDir] = "",
+ [kXDGRuntimeDir] = NULL,
[kXDGConfigDirs] = "/etc/xdg/",
[kXDGDataDirs] = "/usr/local/share/:/usr/share/",
#endif
@@ -66,12 +66,21 @@ char *stdpaths_get_xdg_var(const XDGVarType idx)
/// @param[in] idx XDG directory to use.
///
/// @return [allocated] `{xdg_directory}/nvim`
+///
+/// In WIN32 get_xdg_home(kXDGDataHome) returns `{xdg_directory}/nvim-data` to
+/// avoid storing configuration and data files in the same path.
static char *get_xdg_home(const XDGVarType idx)
FUNC_ATTR_WARN_UNUSED_RESULT
{
char *dir = stdpaths_get_xdg_var(idx);
if (dir) {
+#if defined(WIN32)
+ dir = concat_fnames_realloc(dir,
+ (idx == kXDGDataHome ? "nvim-data" : "nvim"),
+ true);
+#else
dir = concat_fnames_realloc(dir, "nvim", true);
+#endif
}
return dir;
}
@@ -82,7 +91,7 @@ static char *get_xdg_home(const XDGVarType idx)
///
/// @return [allocated] `$XDG_CONFIG_HOME/nvim/{fname}`
char *stdpaths_user_conf_subpath(const char *fname)
- FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
{
return concat_fnames_realloc(get_xdg_home(kXDGConfigHome), fname, true);
}
@@ -95,7 +104,7 @@ char *stdpaths_user_conf_subpath(const char *fname)
/// @return [allocated] `$XDG_DATA_HOME/nvim/{fname}`
char *stdpaths_user_data_subpath(const char *fname,
const size_t trailing_pathseps)
- FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
{
char *ret = concat_fnames_realloc(get_xdg_home(kXDGDataHome), fname, true);
if (trailing_pathseps) {
diff --git a/src/nvim/os/time.c b/src/nvim/os/time.c
index ba1dcf631a..188f0802c9 100644
--- a/src/nvim/os/time.c
+++ b/src/nvim/os/time.c
@@ -93,7 +93,7 @@ struct tm *os_localtime_r(const time_t *restrict clock,
#endif
}
-/// Obtains the current UNIX timestamp and adjusts it to local time
+/// Obtains the current Unix timestamp and adjusts it to local time.
///
/// @param result Pointer to a 'struct tm' where the result should be placed
/// @return A pointer to a 'struct tm' in the current time zone (the 'result'
@@ -104,7 +104,7 @@ struct tm *os_get_localtime(struct tm *result) FUNC_ATTR_NONNULL_ALL
return os_localtime_r(&rawtime, result);
}
-/// Obtains the current UNIX timestamp
+/// Obtains the current Unix timestamp.
///
/// @return Seconds since epoch.
Timestamp os_time(void)
diff --git a/src/nvim/os/unix_defs.h b/src/nvim/os/unix_defs.h
index b1511d4b56..690a39c3cd 100644
--- a/src/nvim/os/unix_defs.h
+++ b/src/nvim/os/unix_defs.h
@@ -1,14 +1,12 @@
#ifndef NVIM_OS_UNIX_DEFS_H
#define NVIM_OS_UNIX_DEFS_H
+// Windows doesn't have unistd.h, so we include it here to avoid numerous
+// instances of `#ifdef WIN32'.
#include <unistd.h>
-#include <signal.h>
-
-// Defines BSD, if it's a BSD system.
-#ifdef HAVE_SYS_PARAM_H
-# include <sys/param.h>
-#endif
+// POSIX.1-2008 says that NAME_MAX should be in here
+#include <limits.h>
#define TEMP_DIR_NAMES {"$TMPDIR", "/tmp", ".", "~"}
#define TEMP_FILE_PATH_MAXLEN 256
@@ -18,21 +16,7 @@
// Special wildcards that need to be handled by the shell.
#define SPECIAL_WILDCHAR "`'{"
-// Unix system-dependent file names
-#ifndef SYS_VIMRC_FILE
-# define SYS_VIMRC_FILE "$VIM/sysinit.vim"
-#endif
-#ifndef DFLT_HELPFILE
-# define DFLT_HELPFILE "$VIMRUNTIME/doc/help.txt"
-#endif
-#ifndef SYNTAX_FNAME
-# define SYNTAX_FNAME "$VIMRUNTIME/syntax/%s.vim"
-#endif
-#ifndef EXRC_FILE
-# define EXRC_FILE ".exrc"
-#endif
-#ifndef VIMRC_FILE
-# define VIMRC_FILE ".nvimrc"
-#endif
+// Separator character for environment variables.
+#define ENV_SEPCHAR ':'
#endif // NVIM_OS_UNIX_DEFS_H
diff --git a/src/nvim/os/users.c b/src/nvim/os/users.c
index 637a86c74f..8ebb7562ef 100644
--- a/src/nvim/os/users.c
+++ b/src/nvim/os/users.c
@@ -42,17 +42,17 @@ int os_get_usernames(garray_T *users)
int os_get_user_name(char *s, size_t len)
{
#ifdef UNIX
- return os_get_uname(getuid(), s, len);
+ return os_get_uname((uv_uid_t)getuid(), s, len);
#else
// TODO(equalsraf): Windows GetUserName()
- return os_get_uname(0, s, len);
+ return os_get_uname((uv_uid_t)0, s, len);
#endif
}
// Insert user name for "uid" in s[len].
// Return OK if a name found.
// If the name is not found, write the uid into s[len] and return FAIL.
-int os_get_uname(uid_t uid, char *s, size_t len)
+int os_get_uname(uv_uid_t uid, char *s, size_t len)
{
#if defined(HAVE_PWD_H) && defined(HAVE_GETPWUID)
struct passwd *pw;
diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h
index d614582250..6a29f86e79 100644
--- a/src/nvim/os/win_defs.h
+++ b/src/nvim/os/win_defs.h
@@ -1,24 +1,33 @@
#ifndef NVIM_OS_WIN_DEFS_H
#define NVIM_OS_WIN_DEFS_H
+// winsock2.h must be first to avoid incompatibilities
+// with winsock.h (included by windows.h)
+#include <winsock2.h>
#include <windows.h>
+#include <sys/stat.h>
+#include <io.h>
+#include <stdio.h>
+
+// Windows does not have S_IFLNK but libuv defines it
+// and sets the flag for us when calling uv_fs_stat.
+#include <uv.h>
+
+#define NAME_MAX _MAX_PATH
#define TEMP_DIR_NAMES {"$TMP", "$TEMP", "$USERPROFILE", ""}
#define TEMP_FILE_PATH_MAXLEN _MAX_PATH
-// Defines needed to fix the build on Windows:
-// - DFLT_DIR
-// - DFLT_BDIR
-// - DFLT_VDIR
-// - EXRC_FILE
-// - VIMRC_FILE
-// - SYNTAX_FNAME
-// - DFLT_HELPFILE
-// - SYS_VIMRC_FILE
-// - SPECIAL_WILDCHAR
+#define FNAME_ILLEGAL "\"*?><|"
+
+// Separator character for environment variables.
+#define ENV_SEPCHAR ';'
#define USE_CRNL
+// We have our own RGB macro in macros.h.
+#undef RGB
+
#ifdef _MSC_VER
# ifndef inline
# define inline __inline
@@ -26,9 +35,22 @@
# ifndef restrict
# define restrict __restrict
# endif
+# ifndef STDOUT_FILENO
+# define STDOUT_FILENO _fileno(stdout)
+# endif
+# ifndef STDERR_FILENO
+# define STDERR_FILENO _fileno(stderr)
+# endif
+# ifndef S_IXUSR
+# define S_IXUSR S_IEXEC
+# endif
#endif
+#define BACKSLASH_IN_FILENAME
+
+#ifdef _MSC_VER
typedef SSIZE_T ssize_t;
+#endif
#ifndef SSIZE_MAX
# ifdef _WIN64
@@ -38,4 +60,30 @@ typedef SSIZE_T ssize_t;
# endif
#endif
+#ifndef O_NOFOLLOW
+# define O_NOFOLLOW 0
+#endif
+
+#if !defined(S_ISDIR) && defined(S_IFDIR)
+# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+#if !defined(S_ISREG) && defined(S_IFREG)
+# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif
+#if !defined(S_ISLNK) && defined(S_IFLNK)
+# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+#endif
+#if !defined(S_ISBLK) && defined(S_IFBLK)
+# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
+#endif
+#if !defined(S_ISSOCK) && defined(S_IFSOCK)
+# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
+#endif
+#if !defined(S_ISFIFO) && defined(S_IFIFO)
+# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
+#endif
+#if !defined(S_ISCHR) && defined(S_IFCHR)
+# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
+#endif
+
#endif // NVIM_OS_WIN_DEFS_H