Repository: microsoft/terminal (language: cpp)

Issue:
Discard old duplicates needs to be case-sensitive
<!-- 
🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨

I ACKNOWLEDGE THE FOLLOWING BEFORE PROCEEDING:
1. If I delete this entire template and go my own path, the core team may close my issue without further explanation or engagement.
2. If I list multiple bugs/concerns in this one issue, the core team may close my issue without further explanation or engagement.
3. If I write an issue that has many duplicates, the core team may close my issue without further explanation or engagement (and without necessarily spending time to find the exact duplicate ID number).
4. If I leave the title incomplete when filing the issue, the core team may close my issue without further explanation or engagement.
5. If I file something completely blank in the body, the core team may close my issue without further explanation or engagement.

All good? Then proceed!
-->

<!--
This bug tracker is monitored by Windows Terminal development team and other technical folks.

**Important: When reporting BSODs or security issues, DO NOT attach memory dumps, logs, or traces to Github issues**.
Instead, send dumps/traces to secure@microsoft.com, referencing this GitHub issue.

If this is an application crash, please also provide a Feedback Hub submission link so we can find your diagnostic data on the backend. Use the category "Apps > Windows Terminal (Preview)" and choose "Share My Feedback" after submission to get the link.

Please use this form and describe your issue, concisely but precisely, with as much detail as possible.

-->


```none
Windows build number: [run `[Environment]::OSVersion` for powershell, or `ver` for cmd]
Windows Terminal version (if applicable):
Microsoft Windows [Version 10.0.18362.535]

Any other software? No, none required
```

# Steps to reproduce

Command Prompt Properties - check "Discard Old Duplicates" in history section
Enable the Experimental Console (UNcheck "Use Legacy Console..."

# Expected behavior

<!-- A description of what you're expecting, possibly containing screenshots or reference material. -->
Run the two commands,
foo /v
foo /V

Then use up-arrow to view the history. You should see BOTH in the history. This is the case for the classic console.

# Actual behavior

<!-- What's actually happening? -->
In the experimental console, only the FIRST instance (foo /v) is preserved. That is, the new console acting case-insensitive, but 'V' and 'v' can have significance to "foo"! 


Discussion / hints:

Fix patch:
diff --git a/src/host/history.cpp b/src/host/history.cpp
index 2b5adf86290..5c655d647e4 100644
--- a/src/host/history.cpp
+++ b/src/host/history.cpp
@@ -72,14 +72,9 @@ void CommandHistory::s_ResizeAll(const size_t commands)
     }
 }
 
-static bool CaseInsensitiveEquality(wchar_t a, wchar_t b)
-{
-    return ::towlower(a) == ::towlower(b);
-}
-
 bool CommandHistory::IsAppNameMatch(const std::wstring_view other) const
 {
-    return std::equal(_appName.cbegin(), _appName.cend(), other.cbegin(), other.cend(), CaseInsensitiveEquality);
+    return CompareStringOrdinal(_appName.data(), gsl::narrow<int>(_appName.size()), other.data(), gsl::narrow<int>(other.size()), TRUE) == CSTR_EQUAL;
 }
 
 // Routine Description:
@@ -534,11 +529,7 @@ std::wstring CommandHistory::Remove(const SHORT iDel)
             const auto& storedCommand = _commands.at(indexFound);
             if ((WI_IsFlagClear(options, MatchOptions::ExactMatch) && (givenCommand.size() <= storedCommand.size())) || (givenCommand.size() == storedCommand.size()))
             {
-                if (std::equal(storedCommand.begin(),
-                               storedCommand.begin() + givenCommand.size(),
-                               givenCommand.begin(),
-                               givenCommand.end(),
-                               CaseInsensitiveEquality))
+                if (til::starts_with(storedCommand, givenCommand))
                 {
                     return true;
                 }
diff --git a/src/host/ut_host/CommandLineTests.cpp b/src/host/ut_host/CommandLineTests.cpp
index 78f253b1b13..6062690ea2a 100644
--- a/src/host/ut_host/CommandLineTests.cpp
+++ b/src/host/ut_host/CommandLineTests.cpp
@@ -450,20 +450,21 @@ class CommandLineTests
         VERIFY_SUCCEEDED(m_pHistory->Add(L"I'm a little teapot", false));
         VERIFY_SUCCEEDED(m_pHistory->Add(L"short and stout", false));
         VERIFY_SUCCEEDED(m_pHistory->Add(L"inflammable", false));
+        VERIFY_SUCCEEDED(m_pHistory->Add(L"Indestructible", false));
 
-        SetPrompt(cookedReadData, L"i");
+        SetPrompt(cookedReadData, L"I");
 
         auto& commandLine = CommandLine::Instance();
         commandLine._cycleMatchingCommandHistoryToPrompt(cookedReadData);
-        VerifyPromptText(cookedReadData, L"inflammable");
+        VerifyPromptText(cookedReadData, L"Indestructible");
 
-        // make sure we skip to the next "i" history item
+        // make sure we skip to the next "I" history item
         commandLine._cycleMatchingCommandHistoryToPrompt(cookedReadData);
         VerifyPromptText(cookedReadData, L"I'm a little teapot");
 
         // should cycle back to the start of the command history
         commandLine._cycleMatchingCommandHistoryToPrompt(cookedReadData);
-        VerifyPromptText(cookedReadData, L"inflammable");
+        VerifyPromptText(cookedReadData, L"Indestructible");
     }
 
     TEST_METHOD(CmdlineCtrlHomeFullwidthChars)


Test patch: