in src/vswhere.lib/VersionRange.cpp [48:190]
STDMETHODIMP VersionRange::ParseVersionRange(
_In_ LPCOLESTR pwszVersionRange,
_Out_ PULONGLONG pullMinVersion,
_Out_ PULONGLONG pullMaxVersion
)
{
HRESULT hr = S_OK;
ULONGLONG ullMinVersion = MinVersion;
ULONGLONG ullMaxVersion = MaxVersion;
LPCWSTR pwszVersionStart = NULL;
LPCWSTR pwszVersionEnd = NULL;
bool sep = false;
bool single = false;
bool isMinInclusive = true;
bool isMaxInclusive = true;
ExitOnNull(pwszVersionRange, hr, E_INVALIDARG, "Missing required parameter: %hs", _nameof(pwszVersionRange));
ExitOnNull(pullMinVersion, hr, E_POINTER, "Output parameter is NULL.");
ExitOnNull(pullMaxVersion, hr, E_POINTER, "Output parameter is NULL.");
*pullMinVersion = 0;
*pullMaxVersion = 0;
LPWSTR pwszVersionRangeEnd = NULL;
pwszVersionRange = Trim(pwszVersionRange, &pwszVersionRangeEnd);
if (pwszVersionRange > pwszVersionRangeEnd)
{
// Should not be possible but check anyway.
ExitOnFailure(hr = E_UNEXPECTED, "Invalid string.");
}
const size_t cchVersionRange = pwszVersionRangeEnd - pwszVersionRange;
ExitOnNull(cchVersionRange, hr, E_INVALIDARG, "Version range required.");
for (size_t i = 0; i < cchVersionRange; ++i)
{
const auto pwsz = &pwszVersionRange[i];
const auto ch = *pwsz;
if (L'(' == ch || L'[' == ch)
{
if (0 != i)
{
ExitOnFailure(hr = E_INVALIDARG, "Invalid format.");
}
else if (L'(' == ch)
{
isMinInclusive = false;
}
}
else if (L')' == ch || L']' == ch)
{
if (cchVersionRange - 1 != i)
{
ExitOnFailure(hr = E_INVALIDARG, "Invalid format.");
}
else if (L')' == ch)
{
isMaxInclusive = false;
}
// Only a single version if the separator was not yet parsed.
single = !sep;
}
else if (L',' == ch)
{
if (sep)
{
// Already parsed a separator.
ExitOnFailure(hr = E_INVALIDARG, "Invalid format.");
}
sep = true;
if (pwszVersionEnd > pwszVersionStart)
{
hr = ParseVersionString(pwszVersionStart, pwszVersionEnd, &ullMinVersion);
ExitOnFailure(hr, "Failed to parse version.");
}
pwszVersionStart = NULL;
pwszVersionEnd = NULL;
}
else if (isspace(ch, neutral_locale))
{
continue;
}
else if (!pwszVersionStart)
{
pwszVersionStart = pwsz;
pwszVersionEnd = pwsz + 1;
}
else
{
pwszVersionEnd = pwsz + 1;
}
}
if (pwszVersionEnd > pwszVersionStart)
{
auto pullVersion = sep ? &ullMaxVersion : &ullMinVersion;
hr = ParseVersionString(pwszVersionStart, pwszVersionEnd, pullVersion);
ExitOnFailure(hr, "Failed to parse version.");
if (single)
{
ullMaxVersion = ullMinVersion;
}
}
if (!isMinInclusive)
{
if (_UI64_MAX == ullMinVersion)
{
ExitOnFailure(hr = E_INVALIDARG, "Arithmetic overflow.");
}
ullMinVersion++;
}
if (!isMaxInclusive)
{
if (0 == ullMaxVersion)
{
ExitOnFailure(hr = E_INVALIDARG, "Arithmetic underflow.");
}
ullMaxVersion--;
}
if (ullMinVersion > ullMaxVersion)
{
ExitOnFailure(hr = E_INVALIDARG, "Min greater than max.");
}
*pullMinVersion = ullMinVersion;
*pullMaxVersion = ullMaxVersion;
Exit:
return hr;
}