aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/buffer.c4
-rw-r--r--src/nvim/eval.c7
-rw-r--r--src/nvim/mbyte.c80
-rw-r--r--src/nvim/os/fs.c96
4 files changed, 181 insertions, 6 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 438a85dd5d..c934d44e70 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -4030,8 +4030,8 @@ void fname_expand(buf_T *buf, char_u **ffname, char_u **sfname)
if (!buf->b_p_bin) {
char_u *rfname;
- /* If the file name is a shortcut file, use the file it links to. */
- rfname = mch_resolve_shortcut(*ffname);
+ // If the file name is a shortcut file, use the file it links to.
+ rfname = os_resolve_shortcut(*ffname);
if (rfname != NULL) {
xfree(*ffname);
*ffname = rfname;
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 8a96278673..a5333d74be 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -13637,11 +13637,12 @@ static void f_resolve(typval_T *argvars, typval_T *rettv)
{
char_u *v = NULL;
- v = mch_resolve_shortcut(p);
- if (v != NULL)
+ v = os_resolve_shortcut(p);
+ if (v != NULL) {
rettv->vval.v_string = v;
- else
+ } else {
rettv->vval.v_string = vim_strsave(p);
+ }
}
#else
# ifdef HAVE_READLINK
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index 0ba9f8b076..26d94aa6fa 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -1885,6 +1885,86 @@ static int utf_strnicmp(char_u *s1, char_u *s2, size_t n1, size_t n2)
return n1 == 0 ? -1 : 1;
}
+#ifdef WIN32
+#ifndef CP_UTF8
+# define CP_UTF8 65001 /* magic number from winnls.h */
+#endif
+
+int utf8_to_utf16(const char *str, WCHAR **strw)
+ FUNC_ATTR_NONNULL_ALL
+{
+ ssize_t wchar_len = 0;
+
+ // Compute the length needed to store the converted widechar string.
+ wchar_len = MultiByteToWideChar(CP_UTF8,
+ 0, // dwFlags: must be 0 for utf8
+ str, // lpMultiByteStr: string to convert
+ -1, // -1 => process up to NUL
+ NULL, // lpWideCharStr: converted string
+ 0); // 0 => return length, don't convert
+ if (wchar_len == 0) {
+ return GetLastError();
+ }
+
+ ssize_t buf_sz = wchar_len * sizeof(WCHAR);
+
+ if (buf_sz == 0) {
+ *strw = NULL;
+ return 0;
+ }
+
+ char *buf = xmalloc(buf_sz);
+ char *pos = buf;
+
+ int r = MultiByteToWideChar(CP_UTF8,
+ 0,
+ str,
+ -1,
+ (WCHAR *)pos,
+ wchar_len);
+ assert(r == wchar_len);
+ *strw = (WCHAR *)pos;
+
+ return 0;
+}
+
+int utf16_to_utf8(const WCHAR *strw, char **str)
+ FUNC_ATTR_NONNULL_ALL
+{
+ // Compute the space required to store the string as UTF-8.
+ ssize_t utf8_len = WideCharToMultiByte(CP_UTF8,
+ 0,
+ strw,
+ -1,
+ NULL,
+ 0,
+ NULL,
+ NULL);
+ if (utf8_len == 0) {
+ return GetLastError();
+ }
+
+ ssize_t buf_sz = utf8_len * sizeof(char);
+ char *buf = xmalloc(buf_sz);
+ char *pos = buf;
+
+ // Convert string to UTF-8.
+ int r = WideCharToMultiByte(CP_UTF8,
+ 0,
+ strw,
+ -1,
+ (LPSTR *)pos,
+ utf8_len,
+ NULL,
+ NULL);
+ assert(r == utf8_len);
+ *str = pos;
+
+ return 0;
+}
+
+#endif
+
/*
* Version of strnicmp() that handles multi-byte characters.
* Needed for Big5, Shift-JIS and UTF-8 encoding. Other DBCS encodings can
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 8c1c80bfad..cd943c4843 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -25,6 +25,10 @@
#include "nvim/path.h"
#include "nvim/strings.h"
+#ifdef WIN32
+#include "nvim/mbyte.h" // for utf8_to_utf16, utf16_to_utf8
+#endif
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/fs.c.generated.h"
#endif
@@ -922,10 +926,100 @@ bool os_fileid_equal(const FileID *file_id_1, const FileID *file_id_2)
/// @param file_info Pointer to a `FileInfo`
/// @return `true` if the `FileID` and the `FileInfo` represent te same file.
bool os_fileid_equal_fileinfo(const FileID *file_id,
- const FileInfo *file_info)
+ const FileInfo *file_info)
FUNC_ATTR_NONNULL_ALL
{
return file_id->inode == file_info->stat.st_ino
&& file_id->device_id == file_info->stat.st_dev;
}
+#ifdef WIN32
+# include <shlobj.h>
+
+/// 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)
+{
+ HRESULT hr;
+ IPersistFile *ppf = NULL;
+ OLECHAR wsz[MAX_PATH];
+ char_u *rfname = NULL;
+ int len;
+ int conversion_result;
+ IShellLinkW *pslw = NULL;
+ WIN32_FIND_DATAW ffdw;
+
+ // Check if the file name ends in ".lnk". Avoid calling CoCreateInstance(),
+ // it's quite slow.
+ if (fname == NULL) {
+ return rfname;
+ }
+ len = (int)STRLEN(fname);
+ if (len <= 4 || STRNICMP(fname + len - 4, ".lnk", 4) != 0) {
+ return rfname;
+ }
+
+ CoInitialize(NULL);
+
+ // create a link manager object and request its interface
+ hr = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+ &IID_IShellLinkW, (void **)&pslw);
+ if (hr == S_OK) {
+ WCHAR *p;
+ int conversion_result = utf8_to_utf16((char *)fname, &p);
+ if (conversion_result != 0) {
+ EMSG2("utf8_to_utf16 failed: %s", uv_strerror(conversion_result));
+ }
+
+ if (p != NULL) {
+ // Get a pointer to the IPersistFile interface.
+ hr = pslw->lpVtbl->QueryInterface(
+ pslw, &IID_IPersistFile, (void **)&ppf);
+ if (hr != S_OK) {
+ goto shortcut_errorw;
+ }
+
+ // "load" the name and resolve the link
+ hr = ppf->lpVtbl->Load(ppf, p, STGM_READ);
+ if (hr != S_OK) {
+ goto shortcut_errorw;
+ }
+
+# if 0 // This makes Vim wait a long time if the target does not exist.
+ hr = pslw->lpVtbl->Resolve(pslw, NULL, SLR_NO_UI);
+ if (hr != S_OK) {
+ goto shortcut_errorw;
+ }
+# endif
+
+ // Get the path to the link target.
+ ZeroMemory(wsz, MAX_PATH * sizeof(WCHAR));
+ hr = pslw->lpVtbl->GetPath(pslw, wsz, MAX_PATH, &ffdw, 0);
+ if (hr == S_OK && wsz[0] != NUL) {
+ int conversion_result = utf16_to_utf8(wsz, &rfname);
+ if (conversion_result != 0) {
+ EMSG2("utf16_to_utf8 failed: %s", uv_strerror(conversion_result));
+ }
+ }
+
+shortcut_errorw:
+ xfree(p);
+ goto shortcut_end;
+ }
+ }
+
+shortcut_end:
+ // Release all interface pointers (both belong to the same object)
+ if (ppf != NULL) {
+ ppf->lpVtbl->Release(ppf);
+ }
+ if (pslw != NULL) {
+ pslw->lpVtbl->Release(pslw);
+ }
+
+ CoUninitialize();
+ return rfname;
+}
+
+#endif