Merge pull request #62 from boostorg/pr/fix-icmp-init-issues

Use ad-hoc extension comparison to avoid initialization issues
This commit is contained in:
Peter Dimov
2018-01-04 05:29:18 +02:00
committed by GitHub

View File

@@ -518,115 +518,28 @@ namespace
|| errval == ERROR_BAD_PATHNAME // "//nosuch" on Win64
|| errval == ERROR_BAD_NETPATH; // "//nosuch" on Win32
}
// File name case-insensitive comparison needs to be locale- and collation-independent.
// The approach used below follows a combined strategy described in the following
// articles:
// http://archives.miloush.net/michkap/archive/2005/10/17/481600.html
// http://archives.miloush.net/michkap/archive/2007/09/14/4900107.html
// http://archives.miloush.net/michkap/archive/2007/10/12/5396685.html
// CompareStringOrdinal is only available on newer systems and is just a wrapper of
// RtlCompareUnicodeString, but measurements showed that RtlEqualUnicodeString has better
// performance. Therefore we use RtlEqualUnicodeString, and if that does not exist
// we perform the equivalent characterwise comparsion using LCMapString and uppercase
// binary equality. Instead of calling RtlInitUnicodeString we use wcslen directly
// because that results in better performance as well.
// Windows ntdll.dll functions that may or may not be present
// must be accessed through pointers
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING;
typedef const UNICODE_STRING *PCUNICODE_STRING;
typedef BOOLEAN (WINAPI *PtrRtlEqualUnicodeString)(
/*_In_*/ PCUNICODE_STRING String1,
/*_In_*/ PCUNICODE_STRING String2,
/*_In_*/ BOOLEAN CaseInSensitive
);
PtrRtlEqualUnicodeString rtl_equal_unicode_string_api = PtrRtlEqualUnicodeString(
::GetProcAddress(
::GetModuleHandleW(L"ntdll.dll"), "RtlEqualUnicodeString"));
#ifndef LOCALE_INVARIANT
# define LOCALE_INVARIANT (MAKELCID(MAKELANGID(LANG_INVARIANT, SUBLANG_NEUTRAL), SORT_DEFAULT))
#endif
bool equal_string_ordinal_ic_1(const wchar_t* s1, const wchar_t* s2)
static bool equal_extension( wchar_t const* p, wchar_t const (&x1)[ 5 ], wchar_t const (&x2)[ 5 ] )
{
std::size_t len1 = std::wcslen(s1);
UNICODE_STRING us1;
us1.Buffer = const_cast<wchar_t*>(s1);
us1.Length = static_cast<USHORT>(sizeof(*s1) * len1);
us1.MaximumLength = static_cast<USHORT>(us1.Length + sizeof(*s1));
std::size_t len2 = std::wcslen(s2);
UNICODE_STRING us2;
us2.Buffer = const_cast<wchar_t*>(s2);
us2.Length = static_cast<USHORT>(sizeof(*s2) * len2);
us2.MaximumLength = static_cast<USHORT>(us2.Length + sizeof(*s2));
BOOLEAN res = rtl_equal_unicode_string_api(&us1, &us2, TRUE);
return res != FALSE;
return
(p[0] == x1[0] || p[0] == x2[0]) &&
(p[1] == x1[1] || p[1] == x2[1]) &&
(p[2] == x1[2] || p[2] == x2[2]) &&
(p[3] == x1[3] || p[3] == x2[3]) &&
p[4] == 0;
}
inline
wchar_t to_upper_invariant(wchar_t input)
{
wchar_t result;
// According to
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd318144(v=vs.85).aspx
// "When transforming between uppercase and lowercase, the function always maps a
// single character to a single character."
int res = ::LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, &input, 1, &result, 1);
if (res != 0)
return result;
assert(!"LCMapStringW failed to convert a character to upper case");
return input; // Should never happen, but this is a safe fallback.
}
bool equal_string_ordinal_ic_2(const wchar_t* s1, const wchar_t* s2)
{
for (;; ++s1, ++s2)
{
const wchar_t c1 = *s1;
const wchar_t c2 = *s2;
if (c1 == c2)
{
if (!c1)
return true; // We have reached the end of both strings, no difference found.
}
else
{
if (!c1 || !c2)
return false; // We have reached the end of one string
// This needs to be upper case to match the behavior of the operating system,
// see http://archives.miloush.net/michkap/archive/2005/10/17/481600.html
const wchar_t u1 = to_upper_invariant(c1);
const wchar_t u2 = to_upper_invariant(c2);
if (u1 != u2)
return false; // strings are different
}
}
}
typedef bool (*Ptr_equal_string_ordinal_ic)(const wchar_t*, const wchar_t*);
Ptr_equal_string_ordinal_ic equal_string_ordinal_ic =
rtl_equal_unicode_string_api ? equal_string_ordinal_ic_1 : equal_string_ordinal_ic_2;
perms make_permissions(const path& p, DWORD attr)
{
perms prms = fs::owner_read | fs::group_read | fs::others_read;
if ((attr & FILE_ATTRIBUTE_READONLY) == 0)
prms |= fs::owner_write | fs::group_write | fs::others_write;
path ext = p.extension();
if (equal_string_ordinal_ic(ext.c_str(), L".exe")
|| equal_string_ordinal_ic(ext.c_str(), L".com")
|| equal_string_ordinal_ic(ext.c_str(), L".bat")
|| equal_string_ordinal_ic(ext.c_str(), L".cmd"))
wchar_t const* q = ext.c_str();
if (equal_extension(q, L".exe", L".EXE")
|| equal_extension(q, L".com", L".COM")
|| equal_extension(q, L".bat", L".BAT")
|| equal_extension(q, L".cmd", L".CMD"))
prms |= fs::owner_exe | fs::group_exe | fs::others_exe;
return prms;
}