diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/buffer.c | 4 | ||||
-rw-r--r-- | src/nvim/eval.c | 7 | ||||
-rw-r--r-- | src/nvim/os/fs.c | 130 |
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 |