Patch 8.1.1083
Problem: MS-Windows: hang when opening a file on network share.
Solution: Avoid using FindFirstFile(), use GetLongPathNameW(). (Ken Takata,
closes #3923)
Files: src/os_win32.c
*** ../vim-8.1.1082/src/os_win32.c 2019-03-27 21:49:10.761396675 +0100
--- src/os_win32.c 2019-03-30 17:27:31.757250891 +0100
***************
*** 2772,2907 ****
#endif
}
-
/*
! * fname_casew(): Wide version of fname_case(). Set the case of the file name,
! * if it already exists. When "len" is > 0, also expand short to long
! * filenames.
! * Return FAIL if wide functions are not available, OK otherwise.
! * NOTE: much of this is identical to fname_case(), keep in sync!
! */
! static int
! fname_casew(
! WCHAR *name,
! int len)
! {
! WCHAR szTrueName[_MAX_PATH + 2];
! WCHAR szTrueNameTemp[_MAX_PATH + 2];
! WCHAR *ptrue, *ptruePrev;
! WCHAR *porig, *porigPrev;
! int flen;
! WIN32_FIND_DATAW fb;
! HANDLE hFind = INVALID_HANDLE_VALUE;
! int c;
! int slen;
!
! flen = (int)wcslen(name);
! if (flen > _MAX_PATH)
! return OK;
!
! /* slash_adjust(name) not needed, already adjusted by fname_case(). */
!
! /* Build the new name in szTrueName[] one component at a time. */
! porig = name;
! ptrue = szTrueName;
!
! if (iswalpha(porig[0]) && porig[1] == L':')
! {
! /* copy leading drive letter */
! *ptrue++ = *porig++;
! *ptrue++ = *porig++;
! }
! *ptrue = NUL; /* in case nothing follows */
!
! while (*porig != NUL)
! {
! /* copy \ characters */
! while (*porig == psepc)
! *ptrue++ = *porig++;
!
! ptruePrev = ptrue;
! porigPrev = porig;
! while (*porig != NUL && *porig != psepc)
! {
! *ptrue++ = *porig++;
! }
! *ptrue = NUL;
!
! /* To avoid a slow failure append "\*" when searching a directory,
! * server or network share. */
! wcscpy(szTrueNameTemp, szTrueName);
! slen = (int)wcslen(szTrueNameTemp);
! if (*porig == psepc && slen + 2 < _MAX_PATH)
! wcscpy(szTrueNameTemp + slen, L"\\*");
!
! /* Skip "", "." and "..". */
! if (ptrue > ptruePrev
! && (ptruePrev[0] != L'.'
! || (ptruePrev[1] != NUL
! && (ptruePrev[1] != L'.' || ptruePrev[2] != NUL)))
! && (hFind = FindFirstFileW(szTrueNameTemp, &fb))
! != INVALID_HANDLE_VALUE)
! {
! c = *porig;
! *porig = NUL;
!
! /* Only use the match when it's the same name (ignoring case) or
! * expansion is allowed and there is a match with the short name
! * and there is enough room. */
! if (_wcsicoll(porigPrev, fb.cFileName) == 0
! || (len > 0
! && (_wcsicoll(porigPrev, fb.cAlternateFileName) == 0
! && (int)(ptruePrev - szTrueName)
! + (int)wcslen(fb.cFileName) < len)))
! {
! wcscpy(ptruePrev, fb.cFileName);
!
! /* Look for exact match and prefer it if found. Must be a
! * long name, otherwise there would be only one match. */
! while (FindNextFileW(hFind, &fb))
! {
! if (*fb.cAlternateFileName != NUL
! && (wcscoll(porigPrev, fb.cFileName) == 0
! || (len > 0
! && (_wcsicoll(porigPrev,
! fb.cAlternateFileName) == 0
! && (int)(ptruePrev - szTrueName)
! + (int)wcslen(fb.cFileName) < len))))
! {
! wcscpy(ptruePrev, fb.cFileName);
! break;
! }
! }
! }
! FindClose(hFind);
! *porig = c;
! ptrue = ptruePrev + wcslen(ptruePrev);
! }
! }
!
! wcscpy(name, szTrueName);
! return OK;
! }
!
! /*
! * fname_case(): Set the case of the file name, if it already exists.
* When "len" is > 0, also expand short to long filenames.
- * NOTE: much of this is identical to fname_casew(), keep in sync!
*/
void
fname_case(
char_u *name,
int len)
{
! char szTrueName[_MAX_PATH + 2];
! char szTrueNameTemp[_MAX_PATH + 2];
! char *ptrue, *ptruePrev;
! char *porig, *porigPrev;
! int flen;
! WIN32_FIND_DATA fb;
! HANDLE hFind;
! int c;
! int slen;
flen = (int)STRLEN(name);
if (flen == 0)
--- 2772,2789 ----
#endif
}
/*
! * Set the case of the file name, if it already exists.
* When "len" is > 0, also expand short to long filenames.
*/
void
fname_case(
char_u *name,
int len)
{
! int flen;
! WCHAR *p;
! WCHAR buf[_MAX_PATH + 1];
flen = (int)STRLEN(name);
if (flen == 0)
***************
*** 2909,3034 ****
slash_adjust(name);
! if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
! {
! WCHAR *p = enc_to_utf16(name, NULL);
!
! if (p != NULL)
! {
! char_u *q;
! WCHAR buf[_MAX_PATH + 1];
!
! wcsncpy(buf, p, _MAX_PATH);
! buf[_MAX_PATH] = L'\0';
! vim_free(p);
!
! if (fname_casew(buf, (len > 0) ? _MAX_PATH : 0) == OK)
! {
! q = utf16_to_enc(buf, NULL);
! if (q != NULL)
! {
! vim_strncpy(name, q, (len > 0) ? len - 1 : flen);
! vim_free(q);
! return;
! }
! }
! }
! return;
! }
!
! /* If 'enc' is utf-8, flen can be larger than _MAX_PATH.
! * So we should check this after calling wide function. */
! if (flen > _MAX_PATH)
return;
! /* Build the new name in szTrueName[] one component at a time. */
! porig = (char *)name;
! ptrue = szTrueName;
!
! if (isalpha(porig[0]) && porig[1] == ':')
{
! /* copy leading drive letter */
! *ptrue++ = *porig++;
! *ptrue++ = *porig++;
! }
! *ptrue = NUL; /* in case nothing follows */
!
! while (*porig != NUL)
! {
! /* copy \ characters */
! while (*porig == psepc)
! *ptrue++ = *porig++;
! ptruePrev = ptrue;
! porigPrev = porig;
! while (*porig != NUL && *porig != psepc)
{
! int l;
!
! if (enc_dbcs)
! {
! l = (*mb_ptr2len)((char_u *)porig);
! while (--l >= 0)
! *ptrue++ = *porig++;
! }
! else
! *ptrue++ = *porig++;
! }
! *ptrue = NUL;
!
! /* To avoid a slow failure append "\*" when searching a directory,
! * server or network share. */
! STRCPY(szTrueNameTemp, szTrueName);
! slen = (int)strlen(szTrueNameTemp);
! if (*porig == psepc && slen + 2 < _MAX_PATH)
! STRCPY(szTrueNameTemp + slen, "\\*");
!
! /* Skip "", "." and "..". */
! if (ptrue > ptruePrev
! && (ptruePrev[0] != '.'
! || (ptruePrev[1] != NUL
! && (ptruePrev[1] != '.' || ptruePrev[2] != NUL)))
! && (hFind = FindFirstFile(szTrueNameTemp, &fb))
! != INVALID_HANDLE_VALUE)
! {
! c = *porig;
! *porig = NUL;
!
! /* Only use the match when it's the same name (ignoring case) or
! * expansion is allowed and there is a match with the short name
! * and there is enough room. */
! if (_stricoll(porigPrev, fb.cFileName) == 0
! || (len > 0
! && (_stricoll(porigPrev, fb.cAlternateFileName) == 0
! && (int)(ptruePrev - szTrueName)
! + (int)strlen(fb.cFileName) < len)))
! {
! STRCPY(ptruePrev, fb.cFileName);
!
! /* Look for exact match and prefer it if found. Must be a
! * long name, otherwise there would be only one match. */
! while (FindNextFile(hFind, &fb))
! {
! if (*fb.cAlternateFileName != NUL
! && (strcoll(porigPrev, fb.cFileName) == 0
! || (len > 0
! && (_stricoll(porigPrev,
! fb.cAlternateFileName) == 0
! && (int)(ptruePrev - szTrueName)
! + (int)strlen(fb.cFileName) < len))))
! {
! STRCPY(ptruePrev, fb.cFileName);
! break;
! }
! }
! }
! FindClose(hFind);
! *porig = c;
! ptrue = ptruePrev + strlen(ptruePrev);
}
}
!
! STRCPY(name, szTrueName);
}
--- 2791,2812 ----
slash_adjust(name);
! p = enc_to_utf16(name, NULL);
! if (p == NULL)
return;
! if (GetLongPathNameW(p, buf, _MAX_PATH))
{
! char_u *q = utf16_to_enc(buf, NULL);
! if (q != NULL)
{
! if (len > 0 || flen >= (int)STRLEN(q))
! vim_strncpy(name, q, (len > 0) ? len - 1 : flen);
! vim_free(q);
}
}
! vim_free(p);
}
*** ../vim-8.1.1082/src/version.c 2019-03-30 16:39:01.402017674 +0100
--- src/version.c 2019-03-30 17:24:46.354268220 +0100
***************
*** 777,778 ****
--- 777,780 ----
{ /* Add new patch number below this line */
+ /**/
+ 1083,
/**/
--
Send $25.00 for handy leaflet on how to make money by selling leaflets
/// Bram Moolenaar -- Br...@Moolenaar.net --
http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features --
http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language --
http://www.Zimbu.org ///
\\\ help me help AIDS victims --
http://ICCF-Holland.org ///