windows-nt/Source/XPSP1/NT/base/win32/fusion/utils/parser.cpp
2020-09-26 16:20:57 +08:00

738 lines
15 KiB
C++

#include "stdinc.h"
#include "debmacro.h"
#include "ntdef.h"
#include "fusionparser.h"
#include "shlwapi.h"
#if !defined(NUMBER_OF)
#define NUMBER_OF(x) (sizeof(x) / sizeof((x)[0]))
#endif
BOOL
CFusionParser::ParseVersion(
ASSEMBLY_VERSION &rav,
PCWSTR sz,
SIZE_T cch,
bool &rfSyntaxValid
)
{
BOOL fSuccess = FALSE;
FN_TRACE_WIN32(fSuccess);
ULONG cDots = 0;
PCWSTR pszTemp;
SIZE_T cchLeft;
ULONG ulTemp;
ASSEMBLY_VERSION avTemp;
PCWSTR pszLast;
bool fSyntaxValid = true;
rfSyntaxValid = false;
PARAMETER_CHECK((sz != NULL) || (cch == 0));
avTemp.Major = 0;
avTemp.Minor = 0;
avTemp.Revision = 0;
avTemp.Build = 0;
while ((cch != 0) && (sz[cch - 1] == L'\0'))
cch--;
// Unfortunately there isn't a StrChrN(), so we'll look for the dots ourselves...
pszTemp = sz;
cchLeft = cch;
while (cchLeft-- != 0)
{
WCHAR wch = *pszTemp++;
if (wch == L'.')
{
cDots++;
if (cDots >= 4)
{
fSyntaxValid = false;
break;
}
}
else if ((wch < L'0') || (wch > L'9'))
{
fSyntaxValid = false;
break;
}
}
if (fSyntaxValid && (cDots < 3))
fSyntaxValid = false;
if (fSyntaxValid)
{
pszTemp = sz;
pszLast = sz + cch;
ulTemp = 0;
for (;;)
{
WCHAR wch = *pszTemp++;
if (wch == L'.')
break;
ulTemp = (ulTemp * 10) + (wch - L'0');
if (ulTemp > 65535)
{
fSuccess = true;
// rfSyntaxValid implicitly false
ASSERT(!rfSyntaxValid);
goto Exit;
}
}
avTemp.Major = (USHORT) ulTemp;
ulTemp = 0;
for (;;)
{
WCHAR wch = *pszTemp++;
if (wch == L'.')
break;
ulTemp = (ulTemp * 10) + (wch - L'0');
if (ulTemp > 65535)
{
fSuccess = true;
// rfSyntaxValid implicitly false
ASSERT(!rfSyntaxValid);
goto Exit;
}
}
avTemp.Minor = (USHORT) ulTemp;
ulTemp = 0;
for (;;)
{
WCHAR wch = *pszTemp++;
if (wch == L'.')
break;
ulTemp = (ulTemp * 10) + (wch - L'0');
if (ulTemp > 65535)
{
fSuccess = true;
// rfSyntaxValid implicitly false
ASSERT(!rfSyntaxValid);
goto Exit;
}
}
avTemp.Revision = (USHORT) ulTemp;
// Now the tricky bit. We aren't necessarily null-terminated, so we
// have to just look for hitting the end.
ulTemp = 0;
while (pszTemp < pszLast)
{
WCHAR wch = *pszTemp++;
ulTemp = (ulTemp * 10) + (wch - L'0');
if (ulTemp > 65535)
{
fSuccess = true;
// rfSyntaxValid implicitly false
ASSERT(!rfSyntaxValid);
goto Exit;
}
}
avTemp.Build = (USHORT) ulTemp;
rav = avTemp;
}
rfSyntaxValid = fSyntaxValid;
fSuccess = TRUE;
Exit:
return fSuccess;
}
#if FUSION_DISABLED_CODE
HRESULT CFusionParser::ParseThreadingModel(ULONG &threadingModel, PCWSTR sz, SIZE_T cch)
{
HRESULT hr = NOERROR;
#define FUSIONPARSER_PARSETHREADINGMODELENTRY(x, y) \
{ x, NUMBER_OF(x) - 1, y },
static const struct
{
PCWSTR m_psz;
SIZE_T m_cch;
ULONG m_threadingModel;
} s_rgmap[] =
{
FUSIONPARSER_PARSETHREADINGMODELENTRY(L"Free", COMCLASS_THREADINGMODEL_FREE)
FUSIONPARSER_PARSETHREADINGMODELENTRY(L"Apartment", COMCLASS_THREADINGMODEL_APARTMENT)
FUSIONPARSER_PARSETHREADINGMODELENTRY(L"Both", COMCLASS_THREADINGMODEL_BOTH)
};
ULONG i;
// If the caller included the terminating null character, back up one.
while ((cch != 0) && (sz[cch - 1] == L'\0'))
cch--;
for (i=0; i<NUMBER_OF(s_rgmap); i++)
{
if ((s_rgmap[i].m_cch == cch) &&
(StrCmpNI(s_rgmap[i].m_psz, sz, cch) == 0))
break;
}
if (i == NUMBER_OF(s_rgmap))
{
// ParseError
hr = E_FAIL;
goto Exit;
}
threadingModel = s_rgmap[i].m_threadingModel;
hr = NOERROR;
Exit:
return hr;
}
HRESULT CFusionParser::ParseBoolean(BOOLEAN &rfValue, PCWSTR sz, SIZE_T cch)
{
HRESULT hr = NOERROR;
static const struct
{
PCWSTR m_psz;
SIZE_T m_cch;
BOOLEAN m_fValue;
} s_rgmap[] =
{
{ L"yes", 3, TRUE },
{ L"no", 2, FALSE },
};
ULONG i;
if (cch < 0)
cch = ::wcslen(sz);
// Some callers may erroneously include the null character in the length...
while ((cch != 0) && (sz[cch - 1] == L'\0'))
cch--;
for (i=0; i<NUMBER_OF(s_rgmap); i++)
{
if ((cch == s_rgmap[i].m_cch) &&
(StrCmpNIW(s_rgmap[i].m_psz, sz, cch) == 0))
break;
}
if (i == NUMBER_OF(s_rgmap))
{
// ParseError
hr = E_FAIL;
goto Exit;
}
rfValue = s_rgmap[i].m_fValue;
hr = NOERROR;
Exit:
return hr;
}
HRESULT CFusionParser::ParseBLOB(BLOB &rblob, PCWSTR szIn, SSIZE_T cch)
{
HRESULT hr = NOERROR;
PCWSTR psz;
WCHAR wch;
SIZE_T cchLeft;
BYTE *pBlobData = NULL;
ULONG cbSize = 0;
psz = szIn;
if (cch < 0)
cch = ::wcslen(szIn);
if ((cch != 0) && (szIn[cch - 1] == L'\0'))
cch--;
cchLeft = cch;
// Run through the string twice; once to count bytes and the second time to
// parse them.
while (cchLeft-- != 0)
{
wch = *psz++;
// Space and dash are nice separators; just ignore them.
if ((wch == L' ') ||
(wch == L'-'))
continue;
if (!SxspIsHexDigit(wch))
{
// Bad character; punt.
// ParseError
hr = E_FAIL;
goto Exit;
}
// First character was good; look for the 2nd nibble
wch = *psz++;
if ((wch == L'\0') ||
(wch == L'-') ||
(wch == L' '))
{
cbSize++;
if (wch == L'\0')
break;
}
else if (!SxspIsHexDigit(wch))
{
// ParseError
hr = E_FAIL;
goto Exit;
}
else
{
cbSize++;
}
}
if (cbSize != 0)
{
BYTE *pb = NULL;
// Allocate the buffer and parse the string for real this time.
pBlobData = NEW(BYTE[cbSize]);
if (pBlobData == NULL)
{
hr = E_OUTOFMEMORY;
goto Exit;
}
// We do not attempt to clean up pBlobData in the exit path, so
// if you add code here that can fail, be sure to clean up pBlobData
// if you do fail.
psz = szIn;
cchLeft = cch;
pb = pBlobData;
while (cchLeft-- != 0)
{
wch = *psz++;
// Space and dash are nice separators; just ignore them.
if ((wch == L' ') ||
(wch == L'-'))
continue;
bTemp = SxspHexDigitToValue(wch);
wch = *psz++;
if ((wch == L'\0') ||
(wch == L'-') ||
(wch == L' '))
{
*pb++ = bTemp;
if (wch == L'\0')
break;
}
*pb++ = (bTemp << 4) | SxspHexDigitToValue(wch);
}
}
rblob.pBlobData = pBlobData;
rblob.cbSize = cbSize;
hr = NOERROR;
Exit:
// No need to clean up pBlobData; we don't fail after its attempted allocation.
return hr;
}
#endif // FUSION_DISABLED_CODE
BOOL
CFusionParser::ParseULONG(
ULONG &rul,
PCWSTR sz,
SIZE_T cch,
ULONG Radix
)
{
BOOL fSuccess = FALSE;
FN_TRACE_WIN32(fSuccess);
ULONG ulTemp = 0;
PARAMETER_CHECK((Radix >= 2) && (Radix <= 36));
while (cch != 0)
{
const WCHAR wch = *sz++;
ULONG Digit = 0;
cch--;
if ((wch >= L'0') && (wch <= L'9'))
Digit = (wch - L'0');
else if ((wch >= L'a') && (wch <= L'z'))
Digit = (10 + wch - L'a');
else if ((wch >= L'A') && (wch <= L'Z'))
Digit = (10 + wch - L'A');
else
ORIGINATE_WIN32_FAILURE_AND_EXIT(InvalidDigit, ERROR_SXS_MANIFEST_PARSE_ERROR);
if (Digit >= Radix)
ORIGINATE_WIN32_FAILURE_AND_EXIT(InvalidDigitForRadix, ERROR_SXS_MANIFEST_PARSE_ERROR);
ulTemp = (ulTemp * Radix) + Digit;
}
rul = ulTemp;
fSuccess = TRUE;
Exit:
return fSuccess;
}
BOOL
CFusionParser::ParseIETFDate(
FILETIME &rft,
PCWSTR sz,
SIZE_T cch
)
{
BOOL fSuccess = FALSE;
FN_TRACE_WIN32(fSuccess);
SYSTEMTIME st;
ULONG ulTemp;
//
// Our format is:
//
// DD/MM/YYYY
// 0123456789
//
// Strip off extra \0's from the end of sz, adjusting the cch
//
while ( ( cch != 0 ) && ( sz[cch - 1] == L'\0' ) )
cch--;
PARAMETER_CHECK(cch == 10);
PARAMETER_CHECK(sz[2] == L'/');
PARAMETER_CHECK(sz[5] == L'/');
ZeroMemory( &st, sizeof( st ) );
IFW32FALSE_EXIT(CFusionParser::ParseULONG(ulTemp, sz, 2, 10));
st.wDay = (WORD)ulTemp;
IFW32FALSE_EXIT(CFusionParser::ParseULONG(ulTemp, sz + 3, 2, 10));
st.wMonth = (WORD)ulTemp;
IFW32FALSE_EXIT(CFusionParser::ParseULONG(ulTemp, sz + 6, 4, 10));
st.wYear = (WORD)ulTemp;
IFW32FALSE_ORIGINATE_AND_EXIT(::SystemTimeToFileTime(&st, &rft));
fSuccess = TRUE;
Exit:
return fSuccess;
}
BOOL
CFusionParser::ParseFILETIME(
FILETIME &rft,
PCWSTR sz,
SIZE_T cch
)
{
BOOL fSuccess = FALSE;
FN_TRACE_WIN32(fSuccess);
SYSTEMTIME st;
ULONG ulTemp;
while ((cch != 0) && (sz[cch - 1] == L'\0'))
cch--;
// MM/DD/YYYY HH:MM:SS.CCC
// 01234567890123456789012
if (cch != 23) // 23 is the exact length of a filetime that we require
ORIGINATE_WIN32_FAILURE_AND_EXIT(BadFiletimeLength, ERROR_SXS_MANIFEST_PARSE_ERROR);
st.wMonth = 0;
st.wDay = 0;
st.wYear = 0;
st.wHour = 0;
st.wMinute = 0;
st.wSecond = 0;
st.wMilliseconds = 0;
if ((sz[2] != L'/') ||
(sz[5] != L'/') ||
(sz[10] != L' ') ||
(sz[13] != L':') ||
(sz[16] != L':') ||
(sz[19] != L'.'))
{
::SetLastError(ERROR_SXS_MANIFEST_PARSE_ERROR);
goto Exit;
}
#define PARSE_FIELD(_field, _index, _length) \
do \
{ \
if (!CFusionParser::ParseULONG(ulTemp, &sz[(_index)], (_length))) \
goto Exit; \
st._field = (WORD) ulTemp; \
} while (0)
PARSE_FIELD(wMonth, 0, 2);
PARSE_FIELD(wDay, 3, 2);
PARSE_FIELD(wYear, 6, 4);
PARSE_FIELD(wHour, 11, 2);
PARSE_FIELD(wMinute, 14, 2);
PARSE_FIELD(wSecond, 17, 2);
PARSE_FIELD(wMilliseconds, 20, 3);
IFW32FALSE_ORIGINATE_AND_EXIT(::SystemTimeToFileTime(&st, &rft));
fSuccess = TRUE;
Exit:
return fSuccess;
}
BOOL
FusionDupString(
LPWSTR *ppszOut,
PCWSTR szIn,
SIZE_T cchIn
)
{
BOOL fSuccess = FALSE;
FN_TRACE_WIN32(fSuccess);
if (ppszOut != NULL)
*ppszOut = NULL;
PARAMETER_CHECK((cchIn == 0) || (szIn != NULL));
PARAMETER_CHECK(ppszOut != NULL);
IFALLOCFAILED_EXIT(*ppszOut = FUSION_NEW_ARRAY(WCHAR, cchIn + 1));
if (cchIn != 0)
memcpy(*ppszOut, szIn, cchIn * sizeof(WCHAR));
(*ppszOut)[cchIn] = L'\0';
fSuccess = TRUE;
Exit:
return fSuccess;
}
int SxspHexDigitToValue(WCHAR wch)
{
if ((wch >= L'a') && (wch <= L'f'))
return 10 + (wch - L'a');
else if ((wch >= L'A') && (wch <= 'F'))
return 10 + (wch - L'A');
else if (wch >= '0' && wch <= '9')
return (wch - L'0');
else
return -1;
}
bool SxspIsHexDigit(WCHAR wch)
{
return (((wch >= L'0') && (wch <= L'9')) ||
((wch >= L'a') && (wch <= L'f')) ||
((wch >= L'A') && (wch <= L'F')));
}
#if FUSION_DISABLED_CODE
HRESULT CFusionParser::ParseULARGE_INTEGER(ULARGE_INTEGER &ruli, PCWSTR sz, SIZE_T cch)
{
HRESULT hr = NOERROR;
ULONGLONG ullTemp = 0;
if (cch < 0)
cch = ::wcslen(sz);
while ((cch != 0) && (sz[cch - 1] == L'\0'))
cch--;
while (cch-- != 0)
{
WCHAR wch = *sz++;
// If we see anything other than a digit, we fail. We're not
// atoi(); we're hard core.
if ((wch < L'0') ||
(wch > L'9'))
{
hr = E_FAIL;
goto Exit;
}
// I don't know if I really need all these casts, but I don't know what the
// compiler's documented behavior for expressions of mixed unsigned __int64, int and
// unsigned short types will be. Instead we'll explicitly cast everything to
// unsigned __int64 (e.g. ULONGLONG) and hopefully the right stuff will happen.
ullTemp = (ullTemp * static_cast<ULONGLONG>(10)) + static_cast<ULONGLONG>(wch - L'\0');
}
ruli.QuadPart = ullTemp;
hr = NOERROR;
Exit:
return hr;
}
HRESULT CFusionParser::ParseHexString(PCWSTR sz, SIZE_T cch, DWORD &rdwOut, PCWSTR &rszOut)
{
HRESULT hr = NOERROR;
FN_TRACE_HR(hr);
DWORD dw = 0;
if (cch < 0)
cch = ::wcslen(sz);
while ((cch != 0) && (sz[cch - 1] == L'\0'))
cch--;
while (cch-- != 0)
{
int i = ::SxspHexDigitToValue(*sz++);
INTERNAL_ERROR_CHECK(i >= 0);
dw = (dw << 4) | i;
}
rdwOut = dw;
rszOut = sz;
hr = NOERROR;
Exit:
return hr;
}
HRESULT
FusionCopyString(
WCHAR *prgchBuffer,
SIZE_T *pcchBuffer,
PCWSTR szIn,
SIZE_T cchIn)
{
HRESULT hr = NOERROR;
FN_TRACE_HR(hr);
SIZE_T cch;
PARAMETER_CHECK(pcchBuffer != NULL);
PARAMETER_CHECK((pcchBuffer == NULL) || ((*pcchBuffer == 0) || (prgchBuffer != NULL)));
PARAMETER_CHECK((szIn != NULL) || (cchIn == 0));
if (cchIn < 0)
cchIn = ::wcslen(szIn);
if ((*pcchBuffer) < ((SIZE_T) (cchIn + 1)))
{
*pcchBuffer = cchIn + 1;
ORIGINATE_WIN32_FAILURE_AND_EXIT(NoRoom, ERROR_INSUFFICIENT_BUFFER);
}
memcpy(prgchBuffer, szIn, cchIn * sizeof(WCHAR));
prgchBuffer[cchIn] = L'\0';
*pcchBuffer = (cchIn + 1);
hr = NOERROR;
Exit:
return hr;
}
HRESULT FusionCopyBlob(BLOB *pblobOut, const BLOB &rblobIn)
{
HRESULT hr = NOERROR;
if (pblobOut != NULL)
{
pblobOut->cbSize = 0;
pblobOut->pBlobData = NULL;
}
if (pblobOut == NULL)
{
hr = E_POINTER;
goto Exit;
}
if (rblobIn.cbSize != 0)
{
pblobOut->pBlobData = NEW(BYTE[rblobIn.cbSize]);
if (pblobOut->pBlobData == NULL)
{
hr = E_OUTOFMEMORY;
goto Exit;
}
memcpy(pblobOut->pBlobData, rblobIn.pBlobData, rblobIn.cbSize);
pblobOut->cbSize = rblobIn.cbSize;
}
hr = NOERROR;
Exit:
return hr;
}
VOID FusionFreeBlob(BLOB *pblob)
{
if (pblob != NULL)
{
if (pblob->pBlobData != NULL)
{
CSxsPreserveLastError ple;
delete []pblob->pBlobData;
pblob->pBlobData = NULL;
ple.Restore();
}
pblob->cbSize = 0;
}
}
#endif // FUSION_DISABLED_CODE