To: vim_dev@googlegroups.com Subject: Patch 8.1.1083 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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 -- Bram@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 ///