mirror of
https://github.com/vim/vim.git
synced 2025-07-26 11:04:33 -04:00
patch 8.1.0894: MS-Windows: resolve() does not return a reparse point
Problem: MS-Windows: resolve() does not return a reparse point. Solution: Improve resolve(). (Yasuhiro Matsumoto, closes #3896)
This commit is contained in:
parent
3615abb693
commit
dce1e89be4
@ -7385,6 +7385,9 @@ repeat({expr}, {count}) *repeat()*
|
|||||||
resolve({filename}) *resolve()* *E655*
|
resolve({filename}) *resolve()* *E655*
|
||||||
On MS-Windows, when {filename} is a shortcut (a .lnk file),
|
On MS-Windows, when {filename} is a shortcut (a .lnk file),
|
||||||
returns the path the shortcut points to in a simplified form.
|
returns the path the shortcut points to in a simplified form.
|
||||||
|
When {filename} is a symbolic link or junction point, return
|
||||||
|
the full path to the target. If the target of junction is
|
||||||
|
removed, return {filename}.
|
||||||
On Unix, repeat resolving symbolic links in all path
|
On Unix, repeat resolving symbolic links in all path
|
||||||
components of {filename} and return the simplified result.
|
components of {filename} and return the simplified result.
|
||||||
To cope with link cycles, resolving of symbolic links is
|
To cope with link cycles, resolving of symbolic links is
|
||||||
|
@ -4847,7 +4847,7 @@ fname_expand(
|
|||||||
char_u *rfname;
|
char_u *rfname;
|
||||||
|
|
||||||
// If the file name is a shortcut file, use the file it links to.
|
// If the file name is a shortcut file, use the file it links to.
|
||||||
rfname = mch_resolve_shortcut(*ffname);
|
rfname = mch_resolve_path(*ffname, FALSE);
|
||||||
if (rfname != NULL)
|
if (rfname != NULL)
|
||||||
{
|
{
|
||||||
vim_free(*ffname);
|
vim_free(*ffname);
|
||||||
|
@ -9912,7 +9912,7 @@ f_resolve(typval_T *argvars, typval_T *rettv)
|
|||||||
{
|
{
|
||||||
char_u *v = NULL;
|
char_u *v = NULL;
|
||||||
|
|
||||||
v = mch_resolve_shortcut(p);
|
v = mch_resolve_path(p, TRUE);
|
||||||
if (v != NULL)
|
if (v != NULL)
|
||||||
rettv->vval.v_string = v;
|
rettv->vval.v_string = v;
|
||||||
else
|
else
|
||||||
|
182
src/os_mswin.c
182
src/os_mswin.c
@ -1823,13 +1823,181 @@ mch_print_set_fg(long_u fgcol)
|
|||||||
# include <shlobj.h>
|
# include <shlobj.h>
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
|
typedef enum _FILE_INFO_BY_HANDLE_CLASS_ {
|
||||||
|
FileBasicInfo_,
|
||||||
|
FileStandardInfo_,
|
||||||
|
FileNameInfo_,
|
||||||
|
FileRenameInfo_,
|
||||||
|
FileDispositionInfo_,
|
||||||
|
FileAllocationInfo_,
|
||||||
|
FileEndOfFileInfo_,
|
||||||
|
FileStreamInfo_,
|
||||||
|
FileCompressionInfo_,
|
||||||
|
FileAttributeTagInfo_,
|
||||||
|
FileIdBothDirectoryInfo_,
|
||||||
|
FileIdBothDirectoryRestartInfo_,
|
||||||
|
FileIoPriorityHintInfo_,
|
||||||
|
FileRemoteProtocolInfo_,
|
||||||
|
FileFullDirectoryInfo_,
|
||||||
|
FileFullDirectoryRestartInfo_,
|
||||||
|
FileStorageInfo_,
|
||||||
|
FileAlignmentInfo_,
|
||||||
|
FileIdInfo_,
|
||||||
|
FileIdExtdDirectoryInfo_,
|
||||||
|
FileIdExtdDirectoryRestartInfo_,
|
||||||
|
FileDispositionInfoEx_,
|
||||||
|
FileRenameInfoEx_,
|
||||||
|
MaximumFileInfoByHandleClass_
|
||||||
|
} FILE_INFO_BY_HANDLE_CLASS_;
|
||||||
|
|
||||||
|
typedef struct _FILE_NAME_INFO_ {
|
||||||
|
DWORD FileNameLength;
|
||||||
|
WCHAR FileName[1];
|
||||||
|
} FILE_NAME_INFO_;
|
||||||
|
|
||||||
|
typedef BOOL (WINAPI *pfnGetFileInformationByHandleEx)(
|
||||||
|
HANDLE hFile,
|
||||||
|
FILE_INFO_BY_HANDLE_CLASS_ FileInformationClass,
|
||||||
|
LPVOID lpFileInformation,
|
||||||
|
DWORD dwBufferSize);
|
||||||
|
static pfnGetFileInformationByHandleEx pGetFileInformationByHandleEx = NULL;
|
||||||
|
|
||||||
|
typedef BOOL (WINAPI *pfnGetVolumeInformationByHandleW)(
|
||||||
|
HANDLE hFile,
|
||||||
|
LPWSTR lpVolumeNameBuffer,
|
||||||
|
DWORD nVolumeNameSize,
|
||||||
|
LPDWORD lpVolumeSerialNumber,
|
||||||
|
LPDWORD lpMaximumComponentLength,
|
||||||
|
LPDWORD lpFileSystemFlags,
|
||||||
|
LPWSTR lpFileSystemNameBuffer,
|
||||||
|
DWORD nFileSystemNameSize);
|
||||||
|
static pfnGetVolumeInformationByHandleW pGetVolumeInformationByHandleW = NULL;
|
||||||
|
|
||||||
|
char_u *
|
||||||
|
resolve_reparse_point(char_u *fname)
|
||||||
|
{
|
||||||
|
HANDLE h = INVALID_HANDLE_VALUE;
|
||||||
|
DWORD size;
|
||||||
|
char_u *rfname = NULL;
|
||||||
|
FILE_NAME_INFO_ *nameinfo = NULL;
|
||||||
|
WCHAR buff[MAX_PATH], *volnames = NULL;
|
||||||
|
HANDLE hv;
|
||||||
|
DWORD snfile, snfind;
|
||||||
|
static BOOL loaded = FALSE;
|
||||||
|
|
||||||
|
if (pGetFileInformationByHandleEx == NULL ||
|
||||||
|
pGetVolumeInformationByHandleW == NULL)
|
||||||
|
{
|
||||||
|
HMODULE hmod = GetModuleHandle("kernel32.dll");
|
||||||
|
|
||||||
|
if (loaded == TRUE)
|
||||||
|
return NULL;
|
||||||
|
pGetFileInformationByHandleEx = (pfnGetFileInformationByHandleEx)
|
||||||
|
GetProcAddress(hmod, "GetFileInformationByHandleEx");
|
||||||
|
pGetVolumeInformationByHandleW = (pfnGetVolumeInformationByHandleW)
|
||||||
|
GetProcAddress(hmod, "GetVolumeInformationByHandleW");
|
||||||
|
loaded = TRUE;
|
||||||
|
if (pGetFileInformationByHandleEx == NULL ||
|
||||||
|
pGetVolumeInformationByHandleW == NULL)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
|
||||||
|
{
|
||||||
|
WCHAR *p;
|
||||||
|
|
||||||
|
p = enc_to_utf16(fname, NULL);
|
||||||
|
if (p == NULL)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if ((GetFileAttributesW(p) & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
|
||||||
|
{
|
||||||
|
vim_free(p);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
h = CreateFileW(p, 0, 0, NULL, OPEN_EXISTING,
|
||||||
|
FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||||
|
vim_free(p);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((GetFileAttributes((char*) fname) &
|
||||||
|
FILE_ATTRIBUTE_REPARSE_POINT) == 0)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
h = CreateFile((char*) fname, 0, 0, NULL, OPEN_EXISTING,
|
||||||
|
FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (h == INVALID_HANDLE_VALUE)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
size = sizeof(FILE_NAME_INFO_) + sizeof(WCHAR) * (MAX_PATH - 1);
|
||||||
|
nameinfo = (FILE_NAME_INFO_*)alloc(size + sizeof(WCHAR));
|
||||||
|
if (nameinfo == NULL)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if (!pGetFileInformationByHandleEx(h, FileNameInfo_, nameinfo, size))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
nameinfo->FileName[nameinfo->FileNameLength / sizeof(WCHAR)] = 0;
|
||||||
|
|
||||||
|
if (!pGetVolumeInformationByHandleW(
|
||||||
|
h, NULL, 0, &snfile, NULL, NULL, NULL, 0))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
hv = FindFirstVolumeW(buff, MAX_PATH);
|
||||||
|
if (hv == INVALID_HANDLE_VALUE)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
do {
|
||||||
|
GetVolumeInformationW(
|
||||||
|
buff, NULL, 0, &snfind, NULL, NULL, NULL, 0);
|
||||||
|
if (snfind == snfile)
|
||||||
|
break;
|
||||||
|
} while (FindNextVolumeW(hv, buff, MAX_PATH));
|
||||||
|
|
||||||
|
FindVolumeClose(hv);
|
||||||
|
|
||||||
|
if (snfind != snfile)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
size = 0;
|
||||||
|
if (!GetVolumePathNamesForVolumeNameW(buff, NULL, 0, &size) &&
|
||||||
|
GetLastError() != ERROR_MORE_DATA)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
volnames = (WCHAR*)alloc(size * sizeof(WCHAR));
|
||||||
|
if (!GetVolumePathNamesForVolumeNameW(buff, volnames, size,
|
||||||
|
&size))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
wcscpy(buff, volnames);
|
||||||
|
if (nameinfo->FileName[0] == '\\')
|
||||||
|
wcscat(buff, nameinfo->FileName + 1);
|
||||||
|
else
|
||||||
|
wcscat(buff, nameinfo->FileName);
|
||||||
|
rfname = utf16_to_enc(buff, NULL);
|
||||||
|
|
||||||
|
fail:
|
||||||
|
if (h != INVALID_HANDLE_VALUE)
|
||||||
|
CloseHandle(h);
|
||||||
|
if (nameinfo != NULL)
|
||||||
|
vim_free(nameinfo);
|
||||||
|
if (volnames != NULL)
|
||||||
|
vim_free(volnames);
|
||||||
|
|
||||||
|
return rfname;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When "fname" is the name of a shortcut (*.lnk) resolve the file it points
|
* When "fname" is the name of a shortcut (*.lnk) resolve the file it points
|
||||||
* to and return that name in allocated memory.
|
* to and return that name in allocated memory.
|
||||||
* Otherwise NULL is returned.
|
* Otherwise NULL is returned.
|
||||||
*/
|
*/
|
||||||
char_u *
|
static char_u *
|
||||||
mch_resolve_shortcut(char_u *fname)
|
resolve_shortcut(char_u *fname)
|
||||||
{
|
{
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
IShellLink *psl = NULL;
|
IShellLink *psl = NULL;
|
||||||
@ -1937,6 +2105,16 @@ shortcut_end:
|
|||||||
CoUninitialize();
|
CoUninitialize();
|
||||||
return rfname;
|
return rfname;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char_u *
|
||||||
|
mch_resolve_path(char_u *fname, int reparse_point)
|
||||||
|
{
|
||||||
|
char_u *path = resolve_shortcut(fname);
|
||||||
|
|
||||||
|
if (path == NULL && reparse_point)
|
||||||
|
path = resolve_reparse_point(fname);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if (defined(FEAT_EVAL) && !defined(FEAT_GUI)) || defined(PROTO)
|
#if (defined(FEAT_EVAL) && !defined(FEAT_GUI)) || defined(PROTO)
|
||||||
|
@ -37,7 +37,7 @@ int mch_print_text_out(char_u *p, int len);
|
|||||||
void mch_print_set_font(int iBold, int iItalic, int iUnderline);
|
void mch_print_set_font(int iBold, int iItalic, int iUnderline);
|
||||||
void mch_print_set_bg(long_u bgcol);
|
void mch_print_set_bg(long_u bgcol);
|
||||||
void mch_print_set_fg(long_u fgcol);
|
void mch_print_set_fg(long_u fgcol);
|
||||||
char_u *mch_resolve_shortcut(char_u *fname);
|
char_u *mch_resolve_path(char_u *fname, int reparse_point);
|
||||||
void win32_set_foreground(void);
|
void win32_set_foreground(void);
|
||||||
void serverInitMessaging(void);
|
void serverInitMessaging(void);
|
||||||
void serverSetName(char_u *name);
|
void serverSetName(char_u *name);
|
||||||
|
@ -188,7 +188,7 @@ func Test_strftime()
|
|||||||
call assert_fails('call strftime("%Y", [])', 'E745:')
|
call assert_fails('call strftime("%Y", [])', 'E745:')
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_resolve()
|
func Test_resolve_unix()
|
||||||
if !has('unix')
|
if !has('unix')
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
@ -234,6 +234,103 @@ func Test_resolve()
|
|||||||
call delete('Xlink1')
|
call delete('Xlink1')
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func s:normalize_fname(fname)
|
||||||
|
let ret = substitute(a:fname, '\', '/', 'g')
|
||||||
|
let ret = substitute(ret, '//', '/', 'g')
|
||||||
|
let ret = tolower(ret)
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_resolve_win32()
|
||||||
|
if !has('win32')
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
" test for shortcut file
|
||||||
|
if executable('cscript')
|
||||||
|
new Xfile
|
||||||
|
wq
|
||||||
|
call writefile([
|
||||||
|
\ 'Set fs = CreateObject("Scripting.FileSystemObject")',
|
||||||
|
\ 'Set ws = WScript.CreateObject("WScript.Shell")',
|
||||||
|
\ 'Set shortcut = ws.CreateShortcut("Xlink.lnk")',
|
||||||
|
\ 'shortcut.TargetPath = fs.BuildPath(ws.CurrentDirectory, "Xfile")',
|
||||||
|
\ 'shortcut.Save'
|
||||||
|
\], 'link.vbs')
|
||||||
|
silent !cscript link.vbs
|
||||||
|
call delete('link.vbs')
|
||||||
|
call assert_equal(s:normalize_fname(getcwd() . '\Xfile'), s:normalize_fname(resolve('./Xlink.lnk')))
|
||||||
|
call delete('Xfile')
|
||||||
|
|
||||||
|
call assert_equal(s:normalize_fname(getcwd() . '\Xfile'), s:normalize_fname(resolve('./Xlink.lnk')))
|
||||||
|
call delete('Xlink.lnk')
|
||||||
|
else
|
||||||
|
echomsg 'skipped test for shortcut file'
|
||||||
|
endif
|
||||||
|
|
||||||
|
" remove files
|
||||||
|
call delete('Xlink')
|
||||||
|
call delete('Xdir', 'd')
|
||||||
|
call delete('Xfile')
|
||||||
|
|
||||||
|
" test for symbolic link to a file
|
||||||
|
new Xfile
|
||||||
|
wq
|
||||||
|
silent !mklink Xlink Xfile
|
||||||
|
if !v:shell_error
|
||||||
|
call assert_equal(s:normalize_fname(getcwd() . '\Xfile'), s:normalize_fname(resolve('./Xlink')))
|
||||||
|
call delete('Xlink')
|
||||||
|
else
|
||||||
|
echomsg 'skipped test for symbolic link to a file'
|
||||||
|
endif
|
||||||
|
call delete('Xfile')
|
||||||
|
|
||||||
|
" test for junction to a directory
|
||||||
|
call mkdir('Xdir')
|
||||||
|
silent !mklink /J Xlink Xdir
|
||||||
|
if !v:shell_error
|
||||||
|
call assert_equal(s:normalize_fname(getcwd() . '\Xdir'), s:normalize_fname(resolve(getcwd() . '/Xlink')))
|
||||||
|
|
||||||
|
call delete('Xdir', 'd')
|
||||||
|
|
||||||
|
" test for junction already removed
|
||||||
|
call assert_equal(s:normalize_fname(getcwd() . '\Xlink'), s:normalize_fname(resolve(getcwd() . '/Xlink')))
|
||||||
|
call delete('Xlink')
|
||||||
|
else
|
||||||
|
echomsg 'skipped test for junction to a directory'
|
||||||
|
call delete('Xdir', 'd')
|
||||||
|
endif
|
||||||
|
|
||||||
|
" test for symbolic link to a directory
|
||||||
|
call mkdir('Xdir')
|
||||||
|
silent !mklink /D Xlink Xdir
|
||||||
|
if !v:shell_error
|
||||||
|
call assert_equal(s:normalize_fname(getcwd() . '\Xdir'), s:normalize_fname(resolve(getcwd() . '/Xlink')))
|
||||||
|
|
||||||
|
call delete('Xdir', 'd')
|
||||||
|
|
||||||
|
" test for symbolic link already removed
|
||||||
|
call assert_equal(s:normalize_fname(getcwd() . '\Xlink'), s:normalize_fname(resolve(getcwd() . '/Xlink')))
|
||||||
|
call delete('Xlink')
|
||||||
|
else
|
||||||
|
echomsg 'skipped test for symbolic link to a directory'
|
||||||
|
call delete('Xdir', 'd')
|
||||||
|
endif
|
||||||
|
|
||||||
|
" test for buffer name
|
||||||
|
new Xfile
|
||||||
|
wq
|
||||||
|
silent !mklink Xlink Xfile
|
||||||
|
if !v:shell_error
|
||||||
|
edit Xlink
|
||||||
|
call assert_equal('Xlink', bufname('%'))
|
||||||
|
call delete('Xlink')
|
||||||
|
bw!
|
||||||
|
else
|
||||||
|
echomsg 'skipped test for buffer name'
|
||||||
|
endif
|
||||||
|
call delete('Xfile')
|
||||||
|
endfunc
|
||||||
|
|
||||||
func Test_simplify()
|
func Test_simplify()
|
||||||
call assert_equal('', simplify(''))
|
call assert_equal('', simplify(''))
|
||||||
call assert_equal('/', simplify('/'))
|
call assert_equal('/', simplify('/'))
|
||||||
|
@ -783,6 +783,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
894,
|
||||||
/**/
|
/**/
|
||||||
893,
|
893,
|
||||||
/**/
|
/**/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user