# HG changeset patch # User Bram Moolenaar # Date 1553963405 -3600 # Node ID 602f1888a2303ffc27bf2b52ab76acc81fe4db90 # Parent 008036af96aaf394f207b80aec6e00af96fc6820 patch 8.1.1083: MS-Windows: hang when opening a file on network share commit https://github.com/vim/vim/commit/8bb41b3d062cd315fdd0626dfd6fa68474a96b50 Author: Bram Moolenaar Date: Sat Mar 30 17:28:16 2019 +0100 patch 8.1.1083: MS-Windows: hang when opening a file on network share Problem: MS-Windows: hang when opening a file on network share. Solution: Avoid using FindFirstFile(), use GetLongPathNameW(). (Ken Takata, closes #3923) diff --git a/src/os_win32.c b/src/os_win32.c --- a/src/os_win32.c +++ b/src/os_win32.c @@ -2772,136 +2772,18 @@ mch_check_win( #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. + * 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; + int flen; + WCHAR *p; + WCHAR buf[_MAX_PATH + 1]; flen = (int)STRLEN(name); if (flen == 0) @@ -2909,126 +2791,22 @@ fname_case( 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; - } - } - } + p = enc_to_utf16(name, NULL); + if (p == NULL) 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) + + if (GetLongPathNameW(p, buf, _MAX_PATH)) + { + char_u *q = utf16_to_enc(buf, NULL); + + if (q != NULL) { - int l; - - if (enc_dbcs) - { - l = (*mb_ptr2len)((char_u *)porig); - while (--l >= 0) - *ptrue++ = *porig++; - } - else - *ptrue++ = *porig++; + if (len > 0 || flen >= (int)STRLEN(q)) + vim_strncpy(name, q, (len > 0) ? len - 1 : flen); + vim_free(q); } - *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); + } + vim_free(p); } diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -776,6 +776,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1083, +/**/ 1082, /**/ 1081,