diff options
author | Matthieu Coudron <mattator@gmail.com> | 2020-04-19 14:11:01 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-19 14:11:01 +0200 |
commit | ca02db1f9fac9fd91164af2c9e42e57913f08f9a (patch) | |
tree | e55c05daaf1177cc5d4f713217562d4f4ace15a8 | |
parent | bf0f74586153dfa8d550e1cfefd83ca9e0354171 (diff) | |
parent | b687a6c2b2c096a68b46141947aef29c616f7a1f (diff) | |
download | rneovim-ca02db1f9fac9fd91164af2c9e42e57913f08f9a.tar.gz rneovim-ca02db1f9fac9fd91164af2c9e42e57913f08f9a.tar.bz2 rneovim-ca02db1f9fac9fd91164af2c9e42e57913f08f9a.zip |
Merge pull request #12047 from erw7/fix-resolve-on-windows
Change resolve() to resolve symbolic links on Windows
Neovim worked the same way as vim for shortcuts, but didn't handle symbolic links and junction cases. This PR implements the same behavior for symbolic links and junctions as for vim.
-rw-r--r-- | src/nvim/eval/funcs.c | 7 | ||||
-rw-r--r-- | src/nvim/os/fs.c | 69 |
2 files changed, 75 insertions, 1 deletions
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 21a6904674..2dfc46023a 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -6722,7 +6722,12 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; const char *fname = tv_get_string(&argvars[0]); #ifdef WIN32 - char *const v = os_resolve_shortcut(fname); + char *v = os_resolve_shortcut(fname); + if (v == NULL) { + if (os_is_reparse_point_include(fname)) { + v = os_realpath(fname, v); + } + } rettv->vval.v_string = (char_u *)(v == NULL ? xstrdup(fname) : v); #else # ifdef HAVE_READLINK diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index ae922e4040..873b611151 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -1147,6 +1147,30 @@ bool os_fileid_equal_fileinfo(const FileID *file_id, && file_id->device_id == file_info->stat.st_dev; } +/// Return the canonicalized absolute pathname. +/// +/// @param[in] name Filename to be canonicalized. +/// @param[out] buf Buffer to store the canonicalized values. A minimum length +// of MAXPATHL+1 is required. If it is NULL, memory is +// allocated. In that case, the caller should deallocate this +// buffer. +/// +/// @return pointer to the buf on success, or NULL. +char *os_realpath(const char *name, char *buf) + FUNC_ATTR_NONNULL_ARG(1) +{ + uv_fs_t request; + int result = uv_fs_realpath(&fs_loop, &request, name, NULL); + if (result == kLibuvSuccess) { + if (buf == NULL) { + buf = xmallocz(MAXPATHL); + } + xstrlcpy(buf, request.ptr, MAXPATHL + 1); + } + uv_fs_req_cleanup(&request); + return result == kLibuvSuccess ? buf : NULL; +} + #ifdef WIN32 # include <shlobj.h> @@ -1233,4 +1257,49 @@ shortcut_end: return rfname; } +#define is_path_sep(c) ((c) == L'\\' || (c) == L'/') +/// Returns true if the path contains a reparse point (junction or symbolic +/// link). Otherwise false in returned. +bool os_is_reparse_point_include(const char *path) +{ + wchar_t *p, *q, *utf16_path; + wchar_t buf[MAX_PATH]; + DWORD attr; + bool result = false; + + const int r = utf8_to_utf16(path, -1, &utf16_path); + if (r != 0) { + EMSG2("utf8_to_utf16 failed: %d", r); + return false; + } + + p = utf16_path; + if (isalpha(p[0]) && p[1] == L':' && is_path_sep(p[2])) { + p += 3; + } else if (is_path_sep(p[0]) && is_path_sep(p[1])) { + p += 2; + } + + while (*p != L'\0') { + q = wcspbrk(p, L"\\/"); + if (q == NULL) { + p = q = utf16_path + wcslen(utf16_path); + } else { + p = q + 1; + } + if (q - utf16_path >= MAX_PATH) { + break; + } + wcsncpy(buf, utf16_path, (size_t)(q - utf16_path)); + buf[q - utf16_path] = L'\0'; + attr = GetFileAttributesW(buf); + if (attr != INVALID_FILE_ATTRIBUTES + && (attr & FILE_ATTRIBUTE_REPARSE_POINT) != 0) { + result = true; + break; + } + } + xfree(utf16_path); + return result; +} #endif |