diff options
Diffstat (limited to 'src/nvim/os')
-rw-r--r-- | src/nvim/os/dl.c | 2 | ||||
-rw-r--r-- | src/nvim/os/env.c | 183 | ||||
-rw-r--r-- | src/nvim/os/fs.c | 197 | ||||
-rw-r--r-- | src/nvim/os/fs_defs.h | 10 | ||||
-rw-r--r-- | src/nvim/os/input.c | 30 | ||||
-rw-r--r-- | src/nvim/os/mem.c | 2 | ||||
-rw-r--r-- | src/nvim/os/os_defs.h | 68 | ||||
-rw-r--r-- | src/nvim/os/shell.c | 28 | ||||
-rw-r--r-- | src/nvim/os/signal.c | 12 | ||||
-rw-r--r-- | src/nvim/os/stdpaths.c | 23 | ||||
-rw-r--r-- | src/nvim/os/time.c | 4 | ||||
-rw-r--r-- | src/nvim/os/unix_defs.h | 28 | ||||
-rw-r--r-- | src/nvim/os/users.c | 6 | ||||
-rw-r--r-- | src/nvim/os/win_defs.h | 68 |
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 |