From 690fda9f311f1bbd6eca6fd7dcab3194a3f2b446 Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Wed, 14 Jan 2026 21:39:39 +0900 Subject: [PATCH] [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. --- dll/win32/shlwapi/string.c | 130 ++++++++++++ .../rostests/apitests/shlwapi/CMakeLists.txt | 1 + modules/rostests/apitests/shlwapi/StrToInt.c | 191 ++++++++++++++++++ modules/rostests/apitests/shlwapi/testlist.c | 2 + 4 files changed, 324 insertions(+) create mode 100644 modules/rostests/apitests/shlwapi/StrToInt.c diff --git a/dll/win32/shlwapi/string.c b/dll/win32/shlwapi/string.c index 7c792d66564..16110831c2b 100644 --- a/dll/win32/shlwapi/string.c +++ b/dll/win32/shlwapi/string.c @@ -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 } /************************************************************************* diff --git a/modules/rostests/apitests/shlwapi/CMakeLists.txt b/modules/rostests/apitests/shlwapi/CMakeLists.txt index bd7556a66bb..6b03f10ecc1 100644 --- a/modules/rostests/apitests/shlwapi/CMakeLists.txt +++ b/modules/rostests/apitests/shlwapi/CMakeLists.txt @@ -22,6 +22,7 @@ list(APPEND SOURCE SHPropertyBag.cpp StrDup.c StrFormatByteSizeW.c + StrToInt.c testdata.rc testlist.c) diff --git a/modules/rostests/apitests/shlwapi/StrToInt.c b/modules/rostests/apitests/shlwapi/StrToInt.c new file mode 100644 index 00000000000..d8b76d53446 --- /dev/null +++ b/modules/rostests/apitests/shlwapi/StrToInt.c @@ -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 + */ + +#include +#include + +#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(); +} diff --git a/modules/rostests/apitests/shlwapi/testlist.c b/modules/rostests/apitests/shlwapi/testlist.c index f47c496b2b0..4a4ee6b154c 100644 --- a/modules/rostests/apitests/shlwapi/testlist.c +++ b/modules/rostests/apitests/shlwapi/testlist.c @@ -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 } };