[SHLWAPI][SHLWAPI_APITEST] Rewrite StrToInt* functions (#8591)

JIRA issue: CORE-19278
Reduce Wine test shlwapi:string failures.
- Rewrite StrToIntA, StrToIntW, StrToIntExA, StrToInt64ExA,
  StrToIntExW, and StrToInt64ExW functions.
- Add testcase StrToInt.
This commit is contained in:
Katayama Hirofumi MZ
2026-01-14 21:39:39 +09:00
committed by GitHub
parent 3319a16288
commit 690fda9f31
4 changed files with 324 additions and 0 deletions

View File

@@ -808,6 +808,9 @@ LPWSTR WINAPI StrStrNIW(LPCWSTR lpFirst, LPCWSTR lpSrch, UINT cchMax)
return NULL;
}
#ifdef __REACTOS__
#define IS_DIGIT(c) ((UINT)(c) - '0' <= 9)
#endif
/*************************************************************************
* StrToIntA [SHLWAPI.@]
*
@@ -825,6 +828,28 @@ LPWSTR WINAPI StrStrNIW(LPCWSTR lpFirst, LPCWSTR lpSrch, UINT cchMax)
*/
int WINAPI StrToIntA(LPCSTR lpszStr)
{
#ifdef __REACTOS__
if (!lpszStr)
return 0;
INT result = 0;
BOOL isNegative = FALSE;
if (*lpszStr == '-')
{
isNegative = TRUE;
++lpszStr;
}
while (IS_DIGIT(*lpszStr))
{
result *= 10;
result += (*lpszStr - '0');
++lpszStr;
}
return isNegative ? -result : result;
#else
int iRet = 0;
TRACE("(%s)\n", debugstr_a(lpszStr));
@@ -838,6 +863,7 @@ int WINAPI StrToIntA(LPCSTR lpszStr)
if (*lpszStr == '-' || isdigit(*lpszStr))
StrToIntExA(lpszStr, 0, &iRet);
return iRet;
#endif
}
/*************************************************************************
@@ -847,6 +873,28 @@ int WINAPI StrToIntA(LPCSTR lpszStr)
*/
int WINAPI StrToIntW(LPCWSTR lpszStr)
{
#ifdef __REACTOS__
if (!lpszStr)
return 0;
INT result = 0;
BOOL isNegative = FALSE;
if (*lpszStr == L'-')
{
isNegative = TRUE;
++lpszStr;
}
while (IS_DIGIT(*lpszStr))
{
result *= 10;
result += (*lpszStr - L'0');
++lpszStr;
}
return isNegative ? -result : result;
#else
int iRet = 0;
TRACE("(%s)\n", debugstr_w(lpszStr));
@@ -860,6 +908,7 @@ int WINAPI StrToIntW(LPCWSTR lpszStr)
if (*lpszStr == '-' || isdigitW(*lpszStr))
StrToIntExW(lpszStr, 0, &iRet);
return iRet;
#endif
}
/*************************************************************************
@@ -891,7 +940,11 @@ BOOL WINAPI StrToIntExA(LPCSTR lpszStr, DWORD dwFlags, int *lpiRet)
TRACE("(%s,%08X,%p)\n", debugstr_a(lpszStr), dwFlags, lpiRet);
bRes = StrToInt64ExA(lpszStr, dwFlags, &li);
#ifdef __REACTOS__
if (lpiRet) *lpiRet = bRes ? (INT)li : 0;
#else
if (bRes) *lpiRet = li;
#endif
return bRes;
}
@@ -902,6 +955,17 @@ BOOL WINAPI StrToIntExA(LPCSTR lpszStr, DWORD dwFlags, int *lpiRet)
*/
BOOL WINAPI StrToInt64ExA(LPCSTR lpszStr, DWORD dwFlags, LONGLONG *lpiRet)
{
#ifdef __REACTOS__
if (!lpszStr)
return FALSE;
WCHAR wideBuf[MAX_PATH];
if (MultiByteToWideChar(CP_ACP, 0, lpszStr, -1, wideBuf, _countof(wideBuf)) <= 0)
return FALSE;
wideBuf[_countof(wideBuf) - 1] = UNICODE_NULL; // SECURITY: Avoid buffer overrun
return StrToInt64ExW(wideBuf, dwFlags, lpiRet);
#else
BOOL bNegative = FALSE;
LONGLONG iRet = 0;
@@ -960,6 +1024,7 @@ BOOL WINAPI StrToInt64ExA(LPCSTR lpszStr, DWORD dwFlags, LONGLONG *lpiRet)
}
*lpiRet = bNegative ? -iRet : iRet;
return TRUE;
#endif
}
/*************************************************************************
@@ -975,7 +1040,11 @@ BOOL WINAPI StrToIntExW(LPCWSTR lpszStr, DWORD dwFlags, int *lpiRet)
TRACE("(%s,%08X,%p)\n", debugstr_w(lpszStr), dwFlags, lpiRet);
bRes = StrToInt64ExW(lpszStr, dwFlags, &li);
#ifdef __REACTOS__
if (lpiRet) *lpiRet = bRes ? (INT)li : 0;
#else
if (bRes) *lpiRet = li;
#endif
return bRes;
}
@@ -986,6 +1055,66 @@ BOOL WINAPI StrToIntExW(LPCWSTR lpszStr, DWORD dwFlags, int *lpiRet)
*/
BOOL WINAPI StrToInt64ExW(LPCWSTR lpszStr, DWORD dwFlags, LONGLONG *lpiRet)
{
#ifdef __REACTOS__
if (!lpszStr)
return FALSE;
// Skip spaces
LPCWSTR pch = lpszStr;
while (*pch == L' ' || *pch == L'\n' || *pch == L'\t')
pch++;
BOOL isNegative = FALSE;
if (*pch == L'+' || *pch == L'-')
{
isNegative = (*pch == L'-');
++pch;
}
ULONGLONG value = 0;
LPCWSTR start = pch;
if ((dwFlags & STIF_SUPPORT_HEX) &&
*pch == L'0' && (pch[1] == L'x' || pch[1] == L'X')) // "0x" or "0X"
{
pch += 2;
start = pch;
for (;;)
{
INT digit;
if (IS_DIGIT(*pch)) digit = *pch - L'0';
else if (L'a' <= *pch && *pch <= L'f') digit = *pch - L'a' + 10;
else if (L'A' <= *pch && *pch <= L'F') digit = *pch - L'A' + 10;
else break;
value *= 16;
value += digit;
++pch;
}
isNegative = FALSE;
}
else
{
while (IS_DIGIT(*pch))
{
value *= 10;
value += (*pch - L'0');
++pch;
}
}
if (pch == start)
{
if (lpiRet)
*lpiRet = 0;
return FALSE; // No data
}
if (lpiRet)
*lpiRet = isNegative ? -(LONGLONG)value : (LONGLONG)value;
return TRUE;
#else
BOOL bNegative = FALSE;
LONGLONG iRet = 0;
@@ -1043,6 +1172,7 @@ BOOL WINAPI StrToInt64ExW(LPCWSTR lpszStr, DWORD dwFlags, LONGLONG *lpiRet)
}
*lpiRet = bNegative ? -iRet : iRet;
return TRUE;
#endif
}
/*************************************************************************

View File

@@ -22,6 +22,7 @@ list(APPEND SOURCE
SHPropertyBag.cpp
StrDup.c
StrFormatByteSizeW.c
StrToInt.c
testdata.rc
testlist.c)

View File

@@ -0,0 +1,191 @@
/*
* PROJECT: ReactOS api tests
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: Tests for StrToInt{,Ex,Ex64}{A,W}
* COPYRIGHT: Copyright 2026 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
*/
#include <apitest.h>
#include <shlwapi.h>
#define ok_longlong(expression, result) do { \
long long _value = (expression); \
ok(_value == (result), "Wrong value for '%s', expected: " #result " (0x%llx), got: 0x%llx\n", \
#expression, (long long)(result), _value); \
} while (0)
static VOID TEST_StrToIntA(VOID)
{
INT n0, n1;
LONGLONG n2;
LPCSTR psz;
BOOL ret1, ret2;
n2 = n1 = 0xDEADFACE;
psz = NULL;
n0 = StrToIntA(psz);
ret1 = StrToIntExA(psz, 0, &n1);
ret2 = StrToInt64ExA(psz, 0, &n2);
ok_int(n0, 0);
ok_int(n1, 0);
ok_longlong(n2, 0xFFFFFFFFDEADFACE);
ok_int(ret1, FALSE);
ok_int(ret2, FALSE);
n2 = n1 = 0xDEADFACE;
psz = "";
n0 = StrToIntA(psz);
ret1 = StrToIntExA(psz, 0, &n1);
ret2 = StrToInt64ExA(psz, 0, &n2);
ok_int(n0, 0);
ok_int(n1, 0);
ok_longlong(n2, 0);
ok_int(ret1, FALSE);
ok_int(ret2, FALSE);
n2 = n1 = 0xDEADFACE;
psz = "123";
n0 = StrToIntA(psz);
ret1 = StrToIntExA(psz, 0, &n1);
ret2 = StrToInt64ExA(psz, 0, &n2);
ok_int(n0, 123);
ok_int(n1, 123);
ok_longlong(n2, 123);
ok_int(ret1, TRUE);
ok_int(ret2, TRUE);
n2 = n1 = 0xDEADFACE;
psz = "+123";
n0 = StrToIntA(psz);
ret1 = StrToIntExA(psz, 0, &n1);
ret2 = StrToInt64ExA(psz, 0, &n2);
ok_int(n0, 0);
ok_int(n1, 123);
ok_longlong(n2, 123);
ok_int(ret1, TRUE);
ok_int(ret2, TRUE);
n2 = n1 = 0xDEADFACE;
psz = "-123";
n0 = StrToIntA(psz);
ret1 = StrToIntExA(psz, 0, &n1);
ret2 = StrToInt64ExA(psz, 0, &n2);
ok_int(n0, -123);
ok_int(n1, -123);
ok_longlong(n2, -123);
ok_int(ret1, TRUE);
ok_int(ret2, TRUE);
n2 = n1 = 0xDEADFACE;
psz = " +123";
n0 = StrToIntA(psz);
ret1 = StrToIntExA(psz, 0, &n1);
ret2 = StrToInt64ExA(psz, 0, &n2);
ok_int(n0, 0);
ok_int(n1, 123);
ok_longlong(n2, 123);
ok_int(ret1, TRUE);
ok_int(ret2, TRUE);
n2 = n1 = 0xDEADFACE;
psz = " -123";
n0 = StrToIntA(psz);
ret1 = StrToIntExA(psz, 0, &n1);
ret2 = StrToInt64ExA(psz, 0, &n2);
ok_int(n0, 0);
ok_int(n1, -123);
ok_longlong(n2, -123);
ok_int(ret1, TRUE);
ok_int(ret2, TRUE);
}
static VOID TEST_StrToIntW(VOID)
{
INT n0, n1;
LONGLONG n2;
LPCWSTR psz;
BOOL ret1, ret2;
n2 = n1 = 0xDEADFACE;
psz = NULL;
n0 = StrToIntW(psz);
ret1 = StrToIntExW(psz, 0, &n1);
ret2 = StrToInt64ExW(psz, 0, &n2);
ok_int(n0, 0);
ok_int(n1, 0);
ok_longlong(n2, 0xFFFFFFFFDEADFACE);
ok_int(ret1, FALSE);
ok_int(ret2, FALSE);
n2 = n1 = 0xDEADFACE;
psz = L"";
n0 = StrToIntW(psz);
ret1 = StrToIntExW(psz, 0, &n1);
ret2 = StrToInt64ExW(psz, 0, &n2);
ok_int(n0, 0);
ok_int(n1, 0);
ok_longlong(n2, 0);
ok_int(ret1, FALSE);
ok_int(ret2, FALSE);
n2 = n1 = 0xDEADFACE;
psz = L"123";
n0 = StrToIntW(psz);
ret1 = StrToIntExW(psz, 0, &n1);
ret2 = StrToInt64ExW(psz, 0, &n2);
ok_int(n0, 123);
ok_int(n1, 123);
ok_longlong(n2, 123);
ok_int(ret1, TRUE);
ok_int(ret2, TRUE);
n2 = n1 = 0xDEADFACE;
psz = L"+123";
n0 = StrToIntW(psz);
ret1 = StrToIntExW(psz, 0, &n1);
ret2 = StrToInt64ExW(psz, 0, &n2);
ok_int(n0, 0);
ok_int(n1, 123);
ok_longlong(n2, 123);
ok_int(ret1, TRUE);
ok_int(ret2, TRUE);
n2 = n1 = 0xDEADFACE;
psz = L"-123";
n0 = StrToIntW(psz);
ret1 = StrToIntExW(psz, 0, &n1);
ret2 = StrToInt64ExW(psz, 0, &n2);
ok_int(n0, -123);
ok_int(n1, -123);
ok_longlong(n2, -123);
ok_int(ret1, TRUE);
ok_int(ret2, TRUE);
n2 = n1 = 0xDEADFACE;
psz = L" +123";
n0 = StrToIntW(psz);
ret1 = StrToIntExW(psz, 0, &n1);
ret2 = StrToInt64ExW(psz, 0, &n2);
ok_int(n0, 0);
ok_int(n1, 123);
ok_longlong(n2, 123);
ok_int(ret1, TRUE);
ok_int(ret2, TRUE);
n2 = n1 = 0xDEADFACE;
psz = L" -123";
n0 = StrToIntW(psz);
ret1 = StrToIntExW(psz, 0, &n1);
ret2 = StrToInt64ExW(psz, 0, &n2);
ok_int(n0, 0);
ok_int(n1, -123);
ok_longlong(n2, -123);
ok_int(ret1, TRUE);
ok_int(ret2, TRUE);
}
START_TEST(StrToInt)
{
TEST_StrToIntA();
TEST_StrToIntW();
}

View File

@@ -18,6 +18,7 @@ extern void func_SHLoadRegUIString(void);
extern void func_SHPropertyBag(void);
extern void func_StrDup(void);
extern void func_StrFormatByteSizeW(void);
extern void func_StrToInt(void);
const struct test winetest_testlist[] =
{
@@ -38,5 +39,6 @@ const struct test winetest_testlist[] =
{ "SHPropertyBag", func_SHPropertyBag },
{ "StrDup", func_StrDup },
{ "StrFormatByteSizeW", func_StrFormatByteSizeW },
{ "StrToInt", func_StrToInt },
{ 0, 0 }
};