aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/buffer.c4
-rw-r--r--src/nvim/eval.c7
-rw-r--r--src/nvim/os/fs.c130
3 files changed, 136 insertions, 5 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 824b298592..7deb1c1bbb 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -13592,11 +13592,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/os/fs.c b/src/nvim/os/fs.c
index 8c1c80bfad..2199b83108 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -929,3 +929,133 @@ bool os_fileid_equal_fileinfo(const FileID *file_id,
&& 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;
+ IShellLink *psl = NULL;
+ IPersistFile *ppf = NULL;
+ OLECHAR wsz[MAX_PATH];
+ WIN32_FIND_DATA ffd; // we get those free of charge
+ CHAR buf[MAX_PATH]; // could have simply reused 'wsz'...
+ char_u *rfname = NULL;
+ int len;
+ IShellLinkW *pslw = NULL;
+ WIN32_FIND_DATAW ffdw; // we get those free of charge
+
+ // 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);
+
+# ifdef FEAT_MBYTE
+ if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) {
+ // 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 = enc_to_utf16(fname, NULL);
+
+ 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) {
+ rfname = utf16_to_enc(wsz, NULL);
+ }
+
+shortcut_errorw:
+ xfree(p);
+ goto shortcut_end;
+ }
+ }
+ /* Retry with non-wide function (for Windows 98). */
+ }
+# endif
+
+ // create a link manager object and request its interface
+ hr = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+ &IID_IShellLink, (void**)&psl);
+ if (hr != S_OK) {
+ goto shortcut_end;
+ }
+
+ // Get a pointer to the IPersistFile interface.
+ hr = psl->lpVtbl->QueryInterface(psl, &IID_IPersistFile, (void**)&ppf);
+ if (hr != S_OK) {
+ goto shortcut_end;
+ }
+
+ // full path string must be in Unicode.
+ MultiByteToWideChar(CP_ACP, 0, (LPCSTR)fname, -1, wsz, MAX_PATH);
+
+ // "load" the name and resolve the link
+ hr = ppf->lpVtbl->Load(ppf, wsz, STGM_READ);
+ if (hr != S_OK) {
+ goto shortcut_end;
+ }
+
+# if 0 // This makes Vim wait a long time if the target doesn't exist.
+ hr = psl->lpVtbl->Resolve(psl, NULL, SLR_NO_UI);
+ if (hr != S_OK) {
+ goto shortcut_end;
+ }
+# endif
+
+ // Get the path to the link target.
+ ZeroMemory(buf, MAX_PATH);
+ hr = psl->lpVtbl->GetPath(psl, buf, MAX_PATH, &ffd, 0);
+ if (hr == S_OK && buf[0] != NUL) {
+ rfname = vim_strsave((char_u *)buf);
+ }
+
+shortcut_end:
+ // Release all interface pointers (both belong to the same object)
+ if (ppf != NULL) {
+ ppf->lpVtbl->Release(ppf);
+ }
+ if (psl != NULL) {
+ psl->lpVtbl->Release(psl);
+ }
+# ifdef FEAT_MBYTE
+ if (pslw != NULL) {
+ pslw->lpVtbl->Release(pslw);
+ }
+# endif
+
+ CoUninitialize();
+ return rfname;
+}
+#endif