diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 79bf8cfcea..e8a15d52d2 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -1011,6 +1011,7 @@ MENUITEMINFO MENUITEMINFOW MERGECOPY MERGEPAINT +Metacharacter metadatamatters Metadatas metafile diff --git a/src/modules/powerrename/lib/PowerRenameRegEx.cpp b/src/modules/powerrename/lib/PowerRenameRegEx.cpp index e9ce4fa62a..266fe5af9d 100644 --- a/src/modules/powerrename/lib/PowerRenameRegEx.cpp +++ b/src/modules/powerrename/lib/PowerRenameRegEx.cpp @@ -33,23 +33,27 @@ static std::wstring SanitizeAndNormalize(const std::wstring& input) // Normalize to NFC (Precomposed). // Get the size needed for the normalized string, including null terminator. - int size = NormalizeString(NormalizationC, sanitized.c_str(), -1, nullptr, 0); - if (size <= 0) + int sizeEstimate = NormalizeString(NormalizationC, sanitized.c_str(), -1, nullptr, 0); + if (sizeEstimate <= 0) { return sanitized; // Return unaltered if normalization fails. } // Perform the normalization. std::wstring normalized; - normalized.resize(size); - NormalizeString(NormalizationC, sanitized.c_str(), -1, &normalized[0], size); + normalized.resize(sizeEstimate); + int actualSize = NormalizeString(NormalizationC, sanitized.c_str(), -1, &normalized[0], sizeEstimate); - // Remove the explicit null terminator added by NormalizeString. - if (!normalized.empty() && normalized.back() == L'\0') + if (actualSize <= 0) { - normalized.pop_back(); + // Normalization failed, return sanitized string. + return sanitized; } + // Resize to actual size minus the null terminator. + // actualSize includes the null terminator when input length is -1. + normalized.resize(static_cast(actualSize) - 1); + return normalized; } diff --git a/src/modules/powerrename/unittests/CommonRegExTests.h b/src/modules/powerrename/unittests/CommonRegExTests.h index 4dc078e9b1..392252655d 100644 --- a/src/modules/powerrename/unittests/CommonRegExTests.h +++ b/src/modules/powerrename/unittests/CommonRegExTests.h @@ -695,6 +695,38 @@ TEST_METHOD(VerifyUnicodeAndWhitespaceNormalizationRegex) VerifyNormalizationHelper(UseRegularExpressions); } +TEST_METHOD(VerifyRegexMetacharacterDollarSign) +{ + CComPtr renameRegEx; + Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); + DWORD flags = UseRegularExpressions; + Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); + + PWSTR result = nullptr; + Assert::IsTrue(renameRegEx->PutSearchTerm(L"$") == S_OK); + Assert::IsTrue(renameRegEx->PutReplaceTerm(L"_end") == S_OK); + unsigned long index = {}; + Assert::IsTrue(renameRegEx->Replace(L"test.txt", &result, index) == S_OK); + Assert::AreEqual(L"test.txt_end", result); + CoTaskMemFree(result); +} + +TEST_METHOD(VerifyRegexMetacharacterCaret) +{ + CComPtr renameRegEx; + Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); + DWORD flags = UseRegularExpressions; + Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); + + PWSTR result = nullptr; + Assert::IsTrue(renameRegEx->PutSearchTerm(L"^") == S_OK); + Assert::IsTrue(renameRegEx->PutReplaceTerm(L"start_") == S_OK); + unsigned long index = {}; + Assert::IsTrue(renameRegEx->Replace(L"test.txt", &result, index) == S_OK); + Assert::AreEqual(L"start_test.txt", result); + CoTaskMemFree(result); +} + #ifndef TESTS_PARTIAL }; }