From 0eec78e72d42f728ef1a17a508e7dc63ddf3e846 Mon Sep 17 00:00:00 2001 From: Chester Liu Date: Thu, 4 Feb 2021 14:55:04 +0800 Subject: [PATCH 1/8] Add support for paste filtering and bracketed paste mode" --- src/cascadia/TerminalControl/TermControl.cpp | 59 ++--------- src/types/inc/utils.hpp | 14 +++ src/types/ut_types/UtilsTests.cpp | 83 +++++++++++++++ src/types/utils.cpp | 102 +++++++++++++++++++ 4 files changed, 207 insertions(+), 51 deletions(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 718f0ee07d4..f818173e84a 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -11,6 +11,7 @@ #include #include #include "../../types/inc/GlyphWidth.hpp" +#include "../../types/inc/Utils.hpp" #include "TermControl.g.cpp" #include "TermControlAutomationPeer.h" @@ -2033,63 +2034,19 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation // Method Description: // - Pre-process text pasted (presumably from the clipboard) - // before sending it over the terminal's connection, converting - // Windows-space \r\n line-endings to \r line-endings - // - Also converts \n line-endings to \r line-endings + // before sending it over the terminal's connection. void TermControl::_SendPastedTextToConnection(const std::wstring& wstr) { - // Some notes on this implementation: - // - // - std::regex can do this in a single line, but is somewhat - // overkill for a simple search/replace operation (and its - // performance guarantees aren't exactly stellar) - // - The STL doesn't have a simple string search/replace method. - // This fact is lamentable. - // - We search for \n, and when we find it we copy the string up to - // the \n (but not including it). Then, we check the if the - // previous character is \r, if its not, then we had a lone \n - // and so we append our own \r - - std::wstring stripped; - stripped.reserve(wstr.length()); - - std::wstring::size_type pos = 0; - std::wstring::size_type begin = 0; - - while ((pos = wstr.find(L"\n", pos)) != std::wstring::npos) - { - // copy up to but not including the \n - stripped.append(wstr.cbegin() + begin, wstr.cbegin() + pos); - if (!(pos > 0 && (wstr.at(pos - 1) == L'\r'))) - { - // there was no \r before the \n we did not copy, - // so append our own \r (this effectively replaces the \n - // with a \r) - stripped.push_back(L'\r'); - } - ++pos; - begin = pos; - } + auto option = ::Microsoft::Console::Utils::PasteOption::CarriageReturnNewline | + ::Microsoft::Console::Utils::PasteOption::FilterControlCodes; - // If we entered the while loop even once, begin would be non-zero - // (because we set begin = pos right after incrementing pos) - // So, if begin is still zero at this point it means we never found a newline - // and we can just write the original string - if (begin == 0) + if (_terminal->IsXtermBracketedPasteModeEnabled()) { - _connection.WriteInput(wstr); - } - else - { - // copy over the part after the last \n - stripped.append(wstr.cbegin() + begin, wstr.cend()); - - // we may have removed some characters, so we may not need as much space - // as we reserved earlier - stripped.shrink_to_fit(); - _connection.WriteInput(stripped); + WI_SetFlag(option, ::Microsoft::Console::Utils::PasteOption::Bracketed); } + const auto converted = ::Microsoft::Console::Utils::ConvertPasteString(wstr, option); + _connection.WriteInput(converted); _terminal->ClearSelection(); _terminal->TrySnapOnInput(); } diff --git a/src/types/inc/utils.hpp b/src/types/inc/utils.hpp index f79ff2414da..e24f3df4e9b 100644 --- a/src/types/inc/utils.hpp +++ b/src/types/inc/utils.hpp @@ -52,6 +52,20 @@ namespace Microsoft::Console::Utils bool StringToUint(const std::wstring_view wstr, unsigned int& value); std::vector SplitString(const std::wstring_view wstr, const wchar_t delimiter) noexcept; + enum PasteOption + { + // Convert Windows-space \r\n and \n line-endings to \r line-endings. + CarriageReturnNewline = 1u << 0, + // For security reasons, remove most control characters + FilterControlCodes = 1u << 1, + // Bracketed paste. See: https://www.xfree86.org/current/ctlseqs.html#Bracketed%20Paste%20Mode. + Bracketed = 1u << 2 + }; + + DEFINE_ENUM_FLAG_OPERATORS(PasteOption) + + std::wstring ConvertPasteString(const std::wstring& wstr, const PasteOption option); + constexpr uint16_t EndianSwap(uint16_t value) { return (value & 0xFF00) >> 8 | diff --git a/src/types/ut_types/UtilsTests.cpp b/src/types/ut_types/UtilsTests.cpp index 8170a01dca2..972f20a4ab5 100644 --- a/src/types/ut_types/UtilsTests.cpp +++ b/src/types/ut_types/UtilsTests.cpp @@ -23,6 +23,7 @@ class UtilsTests TEST_METHOD(TestSwapColorPalette); TEST_METHOD(TestGuidToString); TEST_METHOD(TestSplitString); + TEST_METHOD(TestConvertPasteString); TEST_METHOD(TestStringToUint); TEST_METHOD(TestColorFromXTermColor); @@ -131,6 +132,88 @@ void UtilsTests::TestSplitString() VERIFY_ARE_EQUAL(L"789", result.at(2)); } +void UtilsTests::TestConvertPasteString() +{ + // Test carriage return + const std::wstring noNewLine = L"Hello World"; + VERIFY_ARE_EQUAL(L"Hello World", ConvertPasteString(noNewLine, PasteOption::CarriageReturnNewline)); + + const std::wstring singleCR = L"Hello World\r"; + VERIFY_ARE_EQUAL(L"Hello World\r", ConvertPasteString(singleCR, PasteOption::CarriageReturnNewline)); + + const std::wstring singleLF = L"Hello World\n"; + VERIFY_ARE_EQUAL(L"Hello World\r", ConvertPasteString(singleLF, PasteOption::CarriageReturnNewline)); + + const std::wstring singleCRLF = L"Hello World\r\n"; + VERIFY_ARE_EQUAL(L"Hello World\r", ConvertPasteString(singleCRLF, PasteOption::CarriageReturnNewline)); + + const std::wstring multiCR = L"Hello\rWorld\r"; + VERIFY_ARE_EQUAL(L"Hello\rWorld\r", ConvertPasteString(multiCR, PasteOption::CarriageReturnNewline)); + + const std::wstring multiLF = L"Hello\nWorld\n"; + VERIFY_ARE_EQUAL(L"Hello\rWorld\r", ConvertPasteString(multiLF, PasteOption::CarriageReturnNewline)); + + const std::wstring multiCRLF = L"Hello\r\nWorld\r\n"; + VERIFY_ARE_EQUAL(L"Hello\rWorld\r", ConvertPasteString(multiCRLF, PasteOption::CarriageReturnNewline)); + + const std::wstring multiCR_NoNewLine = L"Hello\rWorld\r123"; + VERIFY_ARE_EQUAL(L"Hello\rWorld\r123", ConvertPasteString(multiCR_NoNewLine, PasteOption::CarriageReturnNewline)); + + const std::wstring multiLF_NoNewLine = L"Hello\nWorld\n123"; + VERIFY_ARE_EQUAL(L"Hello\rWorld\r123", ConvertPasteString(multiLF_NoNewLine, PasteOption::CarriageReturnNewline)); + + const std::wstring multiCRLF_NoNewLine = L"Hello\r\nWorld\r\n123"; + VERIFY_ARE_EQUAL(L"Hello\rWorld\r123", ConvertPasteString(multiCRLF_NoNewLine, PasteOption::CarriageReturnNewline)); + + // Test control code filtering + const std::wstring noNewLineWithControlCodes = L"Hello\x01\x02\x03 123"; + VERIFY_ARE_EQUAL(L"Hello 123", ConvertPasteString(noNewLineWithControlCodes, PasteOption::FilterControlCodes)); + + const std::wstring singleCRWithControlCodes = L"Hello World\r\x01\x02\x03 123"; + VERIFY_ARE_EQUAL(L"Hello World\r 123", ConvertPasteString(singleCRWithControlCodes, PasteOption::FilterControlCodes)); + + const std::wstring singleLFWithControlCodes = L"Hello World\n\x01\x02\x03 123"; + VERIFY_ARE_EQUAL(L"Hello World\n 123", ConvertPasteString(singleLFWithControlCodes, PasteOption::FilterControlCodes)); + + const std::wstring singleCRLFWithControlCodes = L"Hello World\r\n\x01\x02\x03 123"; + VERIFY_ARE_EQUAL(L"Hello World\r\n 123", ConvertPasteString(singleCRLFWithControlCodes, PasteOption::FilterControlCodes)); + + VERIFY_ARE_EQUAL(L"Hello World\r 123", ConvertPasteString(singleCRWithControlCodes, PasteOption::CarriageReturnNewline | PasteOption::FilterControlCodes)); + VERIFY_ARE_EQUAL(L"Hello World\r 123", ConvertPasteString(singleLFWithControlCodes, PasteOption::CarriageReturnNewline | PasteOption::FilterControlCodes)); + VERIFY_ARE_EQUAL(L"Hello World\r 123", ConvertPasteString(singleCRLFWithControlCodes, PasteOption::CarriageReturnNewline | PasteOption::FilterControlCodes)); + + const std::wstring multiCRWithControlCodes = L"Hello\r\x01\x02\x03World\r\x01\x02\x03 123"; + VERIFY_ARE_EQUAL(L"Hello\rWorld\r 123", ConvertPasteString(multiCRWithControlCodes, PasteOption::FilterControlCodes)); + + const std::wstring multiLFWithControlCodes = L"Hello\n\x01\x02\x03World\n\x01\x02\x03 123"; + VERIFY_ARE_EQUAL(L"Hello\nWorld\n 123", ConvertPasteString(multiLFWithControlCodes, PasteOption::FilterControlCodes)); + + const std::wstring multiCRLFWithControlCodes = L"Hello\r\nWorld\r\n\x01\x02\x03 123"; + VERIFY_ARE_EQUAL(L"Hello\r\nWorld\r\n 123", ConvertPasteString(multiCRLFWithControlCodes, PasteOption::FilterControlCodes)); + + VERIFY_ARE_EQUAL(L"Hello\rWorld\r 123", ConvertPasteString(multiCRWithControlCodes, PasteOption::CarriageReturnNewline | PasteOption::FilterControlCodes)); + VERIFY_ARE_EQUAL(L"Hello\rWorld\r 123", ConvertPasteString(multiLFWithControlCodes, PasteOption::CarriageReturnNewline | PasteOption::FilterControlCodes)); + VERIFY_ARE_EQUAL(L"Hello\rWorld\r 123", ConvertPasteString(multiCRLFWithControlCodes, PasteOption::CarriageReturnNewline | PasteOption::FilterControlCodes)); + + const std::wstring multiLineWithALotOfControlCodes = L"e\bc\bh\bo\b \b'.\b!\b:\b\b \bke\bS\b \bi3\bl \bld\bK\bo\b -1\b+\b9 +\b2\b-1'\b >\b \b/\bt\bm\bp\b/\bl\bo\bl\b\r\nsleep 1\r\nmd5sum /tmp/lol"; + + VERIFY_ARE_EQUAL(L"echo '.!: keS i3l ldKo -1+9 +2-1' > /tmp/lol\rsleep 1\rmd5sum /tmp/lol", + ConvertPasteString(multiLineWithALotOfControlCodes, PasteOption::CarriageReturnNewline | PasteOption::FilterControlCodes)); + + // Test bracketed paste + VERIFY_ARE_EQUAL(L"\x1b[200~Hello World\x1b[201~", ConvertPasteString(noNewLine, PasteOption::Bracketed)); + VERIFY_ARE_EQUAL(L"\x1b[200~echo '.!: keS i3l ldKo -1+9 +2-1' > /tmp/lol\rsleep 1\rmd5sum /tmp/lol\x1b[201~", + ConvertPasteString(multiLineWithALotOfControlCodes, PasteOption::CarriageReturnNewline | PasteOption::FilterControlCodes | PasteOption::Bracketed)); + // Malicious string that tries to prematurely terminate bracketed + const std::wstring malicous = L"echo\x1b[201~"; + VERIFY_ARE_EQUAL(L"\x1b[200~echo[201~\x1b[201~", ConvertPasteString(malicous, PasteOption::FilterControlCodes | PasteOption::Bracketed)); + + // Test Unicode content + const std::wstring unicodeString = L"你好\r\n\x01世界\x02\r\n123"; + VERIFY_ARE_EQUAL(L"\x1b[200~你好\r世界\r123\x1b[201~", + ConvertPasteString(unicodeString, PasteOption::CarriageReturnNewline | PasteOption::FilterControlCodes | PasteOption::Bracketed)); +} + void UtilsTests::TestStringToUint() { bool success = false; diff --git a/src/types/utils.cpp b/src/types/utils.cpp index fdd4e7deaec..ef4212aa980 100644 --- a/src/types/utils.cpp +++ b/src/types/utils.cpp @@ -428,6 +428,108 @@ catch (...) return {}; } +// Routine Description: +// - Pre-process text pasted (presumably from the clipboard) with provided option. +// Arguments: +// - wstr - String to process. +// - option - option to use. +// Return Value: +// - The result string. +std::wstring Utils::ConvertPasteString(const std::wstring& wstr, const PasteOption option) +{ + std::wstring converted; + converted.reserve(wstr.length()); + + const auto isControlCode = [](wchar_t c) { + if (c >= L'\x20' && c <= L'\x7f') + { + // Printable ASCII characters + DEL. + return false; + } + + if (c > L'\x7f') + { + // Not a control character. + return false; + } + + // All ASCII control characters will be removed except HT(0x09), LF(0x0a), + // CR(0x0d) and DEL(0x7F). + return c >= L'\x00' && c <= L'\x08' || + c >= L'\x0b' && c <= L'\x0c' || + c >= L'\x0e' && c <= L'\x1f'; + }; + + std::wstring::size_type pos = 0; + std::wstring::size_type begin = 0; + + if (WI_IsFlagSet(option, PasteOption::Bracketed)) + { + converted.append(L"\x1b[200~"); + } + + while (pos < wstr.size()) + { + const wchar_t c = wstr.at(pos); + + if (WI_IsFlagSet(option, PasteOption::CarriageReturnNewline) && c == L'\n') + { + // copy up to but not including the \n + converted.append(wstr.cbegin() + begin, wstr.cbegin() + pos); + if (!(pos > 0 && (wstr.at(pos - 1) == L'\r'))) + { + // there was no \r before the \n we did not copy, + // so append our own \r (this effectively replaces the \n + // with a \r) + converted.push_back(L'\r'); + } + ++pos; + begin = pos; + } + else if (WI_IsFlagSet(option, PasteOption::FilterControlCodes) && isControlCode(c)) + { + // copy up to but not including the control character + converted.append(wstr.cbegin() + begin, wstr.cbegin() + pos); + ++pos; + begin = pos; + } + else + { + ++pos; + } + } + + // If we entered the while loop even once, begin would be non-zero + // (because we set begin = pos right after incrementing pos) + // So, if begin is still zero at this point it means we never found a newline + // and we can just write the original string + if (begin == 0) + { + if (WI_IsFlagSet(option, PasteOption::Bracketed)) + { + converted.append(wstr); + converted.append(L"\x1b[201~"); + return converted; + } + else + { + return wstr; + } + } + else + { + converted.append(wstr.cbegin() + begin, wstr.cend()); + if (WI_IsFlagSet(option, PasteOption::Bracketed)) + { + converted.append(L"\x1b[201~"); + } + // we may have removed some characters, so we may not need as much space + // as we reserved earlier + converted.shrink_to_fit(); + return converted; + } +} + // Routine Description: // - Shorthand check if a handle value is null or invalid. // Arguments: From d168f764b8e4528cecbd9d0a18b4616b266fb180 Mon Sep 17 00:00:00 2001 From: Chester Liu Date: Thu, 4 Feb 2021 15:08:55 +0800 Subject: [PATCH 2/8] Spelling --- src/types/ut_types/UtilsTests.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types/ut_types/UtilsTests.cpp b/src/types/ut_types/UtilsTests.cpp index 972f20a4ab5..37c91fa5899 100644 --- a/src/types/ut_types/UtilsTests.cpp +++ b/src/types/ut_types/UtilsTests.cpp @@ -205,8 +205,8 @@ void UtilsTests::TestConvertPasteString() VERIFY_ARE_EQUAL(L"\x1b[200~echo '.!: keS i3l ldKo -1+9 +2-1' > /tmp/lol\rsleep 1\rmd5sum /tmp/lol\x1b[201~", ConvertPasteString(multiLineWithALotOfControlCodes, PasteOption::CarriageReturnNewline | PasteOption::FilterControlCodes | PasteOption::Bracketed)); // Malicious string that tries to prematurely terminate bracketed - const std::wstring malicous = L"echo\x1b[201~"; - VERIFY_ARE_EQUAL(L"\x1b[200~echo[201~\x1b[201~", ConvertPasteString(malicous, PasteOption::FilterControlCodes | PasteOption::Bracketed)); + const std::wstring malicious = L"echo\x1b[201~"; + VERIFY_ARE_EQUAL(L"\x1b[200~echo[201~\x1b[201~", ConvertPasteString(malicious, PasteOption::FilterControlCodes | PasteOption::Bracketed)); // Test Unicode content const std::wstring unicodeString = L"你好\r\n\x01世界\x02\r\n123"; From 5b94939e78aa80238b7dc16310fedcae84dac089 Mon Sep 17 00:00:00 2001 From: Chester Liu Date: Thu, 4 Feb 2021 15:12:59 +0800 Subject: [PATCH 3/8] this is lol --- .github/actions/spelling/expect/expect.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index d166c63148c..337c477a46e 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -149,6 +149,7 @@ bgidx Bgk BGR BGRA +bh BHID biblioscape bigobj @@ -162,6 +163,7 @@ BITOPERATION bitsavers bitset BKCOLOR +bke BKGND Bksp blog @@ -1281,6 +1283,7 @@ Loewen LOGFONT LOGFONTW logissue +lol lowercased loword lparam From 9e2d997bbcd9bcfaf4421d60d7d611280ce3d9f6 Mon Sep 17 00:00:00 2001 From: Chester Liu Date: Thu, 4 Feb 2021 15:20:09 +0800 Subject: [PATCH 4/8] you happy now? --- src/types/ut_types/UtilsTests.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/types/ut_types/UtilsTests.cpp b/src/types/ut_types/UtilsTests.cpp index 37c91fa5899..4a03858664d 100644 --- a/src/types/ut_types/UtilsTests.cpp +++ b/src/types/ut_types/UtilsTests.cpp @@ -195,15 +195,15 @@ void UtilsTests::TestConvertPasteString() VERIFY_ARE_EQUAL(L"Hello\rWorld\r 123", ConvertPasteString(multiLFWithControlCodes, PasteOption::CarriageReturnNewline | PasteOption::FilterControlCodes)); VERIFY_ARE_EQUAL(L"Hello\rWorld\r 123", ConvertPasteString(multiCRLFWithControlCodes, PasteOption::CarriageReturnNewline | PasteOption::FilterControlCodes)); - const std::wstring multiLineWithALotOfControlCodes = L"e\bc\bh\bo\b \b'.\b!\b:\b\b \bke\bS\b \bi3\bl \bld\bK\bo\b -1\b+\b9 +\b2\b-1'\b >\b \b/\bt\bm\bp\b/\bl\bo\bl\b\r\nsleep 1\r\nmd5sum /tmp/lol"; + const std::wstring multiLineWithLotsOfControlCodes = L"e\bc\bh\bo\b \b'.\b!\b:\b\b \bke\bS\b \bi3\bl \bld\bK\bo\b -1\b+\b9 +\b2\b-1'\b >\b \b/\bt\bm\bp\b/\bl\bo\bl\b\r\nsleep 1\r\nmd5sum /tmp/lol"; VERIFY_ARE_EQUAL(L"echo '.!: keS i3l ldKo -1+9 +2-1' > /tmp/lol\rsleep 1\rmd5sum /tmp/lol", - ConvertPasteString(multiLineWithALotOfControlCodes, PasteOption::CarriageReturnNewline | PasteOption::FilterControlCodes)); + ConvertPasteString(multiLineWithLotsOfControlCodes, PasteOption::CarriageReturnNewline | PasteOption::FilterControlCodes)); // Test bracketed paste VERIFY_ARE_EQUAL(L"\x1b[200~Hello World\x1b[201~", ConvertPasteString(noNewLine, PasteOption::Bracketed)); VERIFY_ARE_EQUAL(L"\x1b[200~echo '.!: keS i3l ldKo -1+9 +2-1' > /tmp/lol\rsleep 1\rmd5sum /tmp/lol\x1b[201~", - ConvertPasteString(multiLineWithALotOfControlCodes, PasteOption::CarriageReturnNewline | PasteOption::FilterControlCodes | PasteOption::Bracketed)); + ConvertPasteString(multiLineWithLotsOfControlCodes, PasteOption::CarriageReturnNewline | PasteOption::FilterControlCodes | PasteOption::Bracketed)); // Malicious string that tries to prematurely terminate bracketed const std::wstring malicious = L"echo\x1b[201~"; VERIFY_ARE_EQUAL(L"\x1b[200~echo[201~\x1b[201~", ConvertPasteString(malicious, PasteOption::FilterControlCodes | PasteOption::Bracketed)); From df187f623532d70f69a9cf4d92e9c1ae617b5406 Mon Sep 17 00:00:00 2001 From: Chester Liu Date: Fri, 5 Feb 2021 15:23:37 +0800 Subject: [PATCH 5/8] Feedback --- src/cascadia/TerminalControl/TermControl.cpp | 13 +--- src/cascadia/TerminalCore/Terminal.cpp | 21 +++++++ src/cascadia/TerminalCore/Terminal.hpp | 3 + src/types/inc/utils.hpp | 15 +++-- src/types/ut_types/UtilsTests.cpp | 64 +++++++++----------- src/types/utils.cpp | 48 +++++---------- 6 files changed, 75 insertions(+), 89 deletions(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index f818173e84a..b01bf957e8e 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -2037,18 +2037,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation // before sending it over the terminal's connection. void TermControl::_SendPastedTextToConnection(const std::wstring& wstr) { - auto option = ::Microsoft::Console::Utils::PasteOption::CarriageReturnNewline | - ::Microsoft::Console::Utils::PasteOption::FilterControlCodes; - - if (_terminal->IsXtermBracketedPasteModeEnabled()) - { - WI_SetFlag(option, ::Microsoft::Console::Utils::PasteOption::Bracketed); - } - - const auto converted = ::Microsoft::Console::Utils::ConvertPasteString(wstr, option); - _connection.WriteInput(converted); - _terminal->ClearSelection(); - _terminal->TrySnapOnInput(); + _terminal->WritePastedText(wstr); } // Method Description: diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 505acdc36c7..7965e9e7620 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -397,6 +397,27 @@ void Terminal::Write(std::wstring_view stringView) _stateMachine->ProcessString(stringView); } +void Terminal::WritePastedText(std::wstring_view stringView) +{ + auto option = ::Microsoft::Console::Utils::FilterOption::CarriageReturnNewline | + ::Microsoft::Console::Utils::FilterOption::ControlCodes; + + std::wstring filtered = ::Microsoft::Console::Utils::FilterStringForPaste(stringView, option); + if (IsXtermBracketedPasteModeEnabled()) + { + filtered.insert(0, L"\x1b[200~"); + filtered.append(L"\x1b[201~"); + } + + if (_pfnWriteInput) + { + _pfnWriteInput(filtered); + } + + ClearSelection(); + TrySnapOnInput(); +} + // Method Description: // - Attempts to snap to the bottom of the buffer, if SnapOnInput is true. Does // nothing if SnapOnInput is set to false, or we're already at the bottom of diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 75c865ede71..dcd64d71c88 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -66,6 +66,9 @@ class Microsoft::Terminal::Core::Terminal final : // Write goes through the parser void Write(std::wstring_view stringView); + // WritePastedText goes directly to the connection + void WritePastedText(std::wstring_view stringView); + [[nodiscard]] std::shared_lock LockForReading(); [[nodiscard]] std::unique_lock LockForWriting(); diff --git a/src/types/inc/utils.hpp b/src/types/inc/utils.hpp index e24f3df4e9b..d37b6e076d8 100644 --- a/src/types/inc/utils.hpp +++ b/src/types/inc/utils.hpp @@ -52,19 +52,18 @@ namespace Microsoft::Console::Utils bool StringToUint(const std::wstring_view wstr, unsigned int& value); std::vector SplitString(const std::wstring_view wstr, const wchar_t delimiter) noexcept; - enum PasteOption + enum FilterOption { - // Convert Windows-space \r\n and \n line-endings to \r line-endings. + None = 0, + // Convert CR+LF and LF-only line endings to CR-only. CarriageReturnNewline = 1u << 0, - // For security reasons, remove most control characters - FilterControlCodes = 1u << 1, - // Bracketed paste. See: https://www.xfree86.org/current/ctlseqs.html#Bracketed%20Paste%20Mode. - Bracketed = 1u << 2 + // For security reasons, remove most control characters. + ControlCodes = 1u << 1, }; - DEFINE_ENUM_FLAG_OPERATORS(PasteOption) + DEFINE_ENUM_FLAG_OPERATORS(FilterOption) - std::wstring ConvertPasteString(const std::wstring& wstr, const PasteOption option); + std::wstring FilterStringForPaste(const std::wstring_view wstr, const FilterOption option); constexpr uint16_t EndianSwap(uint16_t value) { diff --git a/src/types/ut_types/UtilsTests.cpp b/src/types/ut_types/UtilsTests.cpp index 4a03858664d..a5fdebd9076 100644 --- a/src/types/ut_types/UtilsTests.cpp +++ b/src/types/ut_types/UtilsTests.cpp @@ -23,7 +23,7 @@ class UtilsTests TEST_METHOD(TestSwapColorPalette); TEST_METHOD(TestGuidToString); TEST_METHOD(TestSplitString); - TEST_METHOD(TestConvertPasteString); + TEST_METHOD(TestFilterStringForPaste); TEST_METHOD(TestStringToUint); TEST_METHOD(TestColorFromXTermColor); @@ -132,86 +132,78 @@ void UtilsTests::TestSplitString() VERIFY_ARE_EQUAL(L"789", result.at(2)); } -void UtilsTests::TestConvertPasteString() +void UtilsTests::TestFilterStringForPaste() { // Test carriage return const std::wstring noNewLine = L"Hello World"; - VERIFY_ARE_EQUAL(L"Hello World", ConvertPasteString(noNewLine, PasteOption::CarriageReturnNewline)); + VERIFY_ARE_EQUAL(L"Hello World", FilterStringForPaste(noNewLine, FilterOption::CarriageReturnNewline)); const std::wstring singleCR = L"Hello World\r"; - VERIFY_ARE_EQUAL(L"Hello World\r", ConvertPasteString(singleCR, PasteOption::CarriageReturnNewline)); + VERIFY_ARE_EQUAL(L"Hello World\r", FilterStringForPaste(singleCR, FilterOption::CarriageReturnNewline)); const std::wstring singleLF = L"Hello World\n"; - VERIFY_ARE_EQUAL(L"Hello World\r", ConvertPasteString(singleLF, PasteOption::CarriageReturnNewline)); + VERIFY_ARE_EQUAL(L"Hello World\r", FilterStringForPaste(singleLF, FilterOption::CarriageReturnNewline)); const std::wstring singleCRLF = L"Hello World\r\n"; - VERIFY_ARE_EQUAL(L"Hello World\r", ConvertPasteString(singleCRLF, PasteOption::CarriageReturnNewline)); + VERIFY_ARE_EQUAL(L"Hello World\r", FilterStringForPaste(singleCRLF, FilterOption::CarriageReturnNewline)); const std::wstring multiCR = L"Hello\rWorld\r"; - VERIFY_ARE_EQUAL(L"Hello\rWorld\r", ConvertPasteString(multiCR, PasteOption::CarriageReturnNewline)); + VERIFY_ARE_EQUAL(L"Hello\rWorld\r", FilterStringForPaste(multiCR, FilterOption::CarriageReturnNewline)); const std::wstring multiLF = L"Hello\nWorld\n"; - VERIFY_ARE_EQUAL(L"Hello\rWorld\r", ConvertPasteString(multiLF, PasteOption::CarriageReturnNewline)); + VERIFY_ARE_EQUAL(L"Hello\rWorld\r", FilterStringForPaste(multiLF, FilterOption::CarriageReturnNewline)); const std::wstring multiCRLF = L"Hello\r\nWorld\r\n"; - VERIFY_ARE_EQUAL(L"Hello\rWorld\r", ConvertPasteString(multiCRLF, PasteOption::CarriageReturnNewline)); + VERIFY_ARE_EQUAL(L"Hello\rWorld\r", FilterStringForPaste(multiCRLF, FilterOption::CarriageReturnNewline)); const std::wstring multiCR_NoNewLine = L"Hello\rWorld\r123"; - VERIFY_ARE_EQUAL(L"Hello\rWorld\r123", ConvertPasteString(multiCR_NoNewLine, PasteOption::CarriageReturnNewline)); + VERIFY_ARE_EQUAL(L"Hello\rWorld\r123", FilterStringForPaste(multiCR_NoNewLine, FilterOption::CarriageReturnNewline)); const std::wstring multiLF_NoNewLine = L"Hello\nWorld\n123"; - VERIFY_ARE_EQUAL(L"Hello\rWorld\r123", ConvertPasteString(multiLF_NoNewLine, PasteOption::CarriageReturnNewline)); + VERIFY_ARE_EQUAL(L"Hello\rWorld\r123", FilterStringForPaste(multiLF_NoNewLine, FilterOption::CarriageReturnNewline)); const std::wstring multiCRLF_NoNewLine = L"Hello\r\nWorld\r\n123"; - VERIFY_ARE_EQUAL(L"Hello\rWorld\r123", ConvertPasteString(multiCRLF_NoNewLine, PasteOption::CarriageReturnNewline)); + VERIFY_ARE_EQUAL(L"Hello\rWorld\r123", FilterStringForPaste(multiCRLF_NoNewLine, FilterOption::CarriageReturnNewline)); // Test control code filtering const std::wstring noNewLineWithControlCodes = L"Hello\x01\x02\x03 123"; - VERIFY_ARE_EQUAL(L"Hello 123", ConvertPasteString(noNewLineWithControlCodes, PasteOption::FilterControlCodes)); + VERIFY_ARE_EQUAL(L"Hello 123", FilterStringForPaste(noNewLineWithControlCodes, FilterOption::ControlCodes)); const std::wstring singleCRWithControlCodes = L"Hello World\r\x01\x02\x03 123"; - VERIFY_ARE_EQUAL(L"Hello World\r 123", ConvertPasteString(singleCRWithControlCodes, PasteOption::FilterControlCodes)); + VERIFY_ARE_EQUAL(L"Hello World\r 123", FilterStringForPaste(singleCRWithControlCodes, FilterOption::ControlCodes)); const std::wstring singleLFWithControlCodes = L"Hello World\n\x01\x02\x03 123"; - VERIFY_ARE_EQUAL(L"Hello World\n 123", ConvertPasteString(singleLFWithControlCodes, PasteOption::FilterControlCodes)); + VERIFY_ARE_EQUAL(L"Hello World\n 123", FilterStringForPaste(singleLFWithControlCodes, FilterOption::ControlCodes)); const std::wstring singleCRLFWithControlCodes = L"Hello World\r\n\x01\x02\x03 123"; - VERIFY_ARE_EQUAL(L"Hello World\r\n 123", ConvertPasteString(singleCRLFWithControlCodes, PasteOption::FilterControlCodes)); + VERIFY_ARE_EQUAL(L"Hello World\r\n 123", FilterStringForPaste(singleCRLFWithControlCodes, FilterOption::ControlCodes)); - VERIFY_ARE_EQUAL(L"Hello World\r 123", ConvertPasteString(singleCRWithControlCodes, PasteOption::CarriageReturnNewline | PasteOption::FilterControlCodes)); - VERIFY_ARE_EQUAL(L"Hello World\r 123", ConvertPasteString(singleLFWithControlCodes, PasteOption::CarriageReturnNewline | PasteOption::FilterControlCodes)); - VERIFY_ARE_EQUAL(L"Hello World\r 123", ConvertPasteString(singleCRLFWithControlCodes, PasteOption::CarriageReturnNewline | PasteOption::FilterControlCodes)); + VERIFY_ARE_EQUAL(L"Hello World\r 123", FilterStringForPaste(singleCRWithControlCodes, FilterOption::CarriageReturnNewline | FilterOption::ControlCodes)); + VERIFY_ARE_EQUAL(L"Hello World\r 123", FilterStringForPaste(singleLFWithControlCodes, FilterOption::CarriageReturnNewline | FilterOption::ControlCodes)); + VERIFY_ARE_EQUAL(L"Hello World\r 123", FilterStringForPaste(singleCRLFWithControlCodes, FilterOption::CarriageReturnNewline | FilterOption::ControlCodes)); const std::wstring multiCRWithControlCodes = L"Hello\r\x01\x02\x03World\r\x01\x02\x03 123"; - VERIFY_ARE_EQUAL(L"Hello\rWorld\r 123", ConvertPasteString(multiCRWithControlCodes, PasteOption::FilterControlCodes)); + VERIFY_ARE_EQUAL(L"Hello\rWorld\r 123", FilterStringForPaste(multiCRWithControlCodes, FilterOption::ControlCodes)); const std::wstring multiLFWithControlCodes = L"Hello\n\x01\x02\x03World\n\x01\x02\x03 123"; - VERIFY_ARE_EQUAL(L"Hello\nWorld\n 123", ConvertPasteString(multiLFWithControlCodes, PasteOption::FilterControlCodes)); + VERIFY_ARE_EQUAL(L"Hello\nWorld\n 123", FilterStringForPaste(multiLFWithControlCodes, FilterOption::ControlCodes)); const std::wstring multiCRLFWithControlCodes = L"Hello\r\nWorld\r\n\x01\x02\x03 123"; - VERIFY_ARE_EQUAL(L"Hello\r\nWorld\r\n 123", ConvertPasteString(multiCRLFWithControlCodes, PasteOption::FilterControlCodes)); + VERIFY_ARE_EQUAL(L"Hello\r\nWorld\r\n 123", FilterStringForPaste(multiCRLFWithControlCodes, FilterOption::ControlCodes)); - VERIFY_ARE_EQUAL(L"Hello\rWorld\r 123", ConvertPasteString(multiCRWithControlCodes, PasteOption::CarriageReturnNewline | PasteOption::FilterControlCodes)); - VERIFY_ARE_EQUAL(L"Hello\rWorld\r 123", ConvertPasteString(multiLFWithControlCodes, PasteOption::CarriageReturnNewline | PasteOption::FilterControlCodes)); - VERIFY_ARE_EQUAL(L"Hello\rWorld\r 123", ConvertPasteString(multiCRLFWithControlCodes, PasteOption::CarriageReturnNewline | PasteOption::FilterControlCodes)); + VERIFY_ARE_EQUAL(L"Hello\rWorld\r 123", FilterStringForPaste(multiCRWithControlCodes, FilterOption::CarriageReturnNewline | FilterOption::ControlCodes)); + VERIFY_ARE_EQUAL(L"Hello\rWorld\r 123", FilterStringForPaste(multiLFWithControlCodes, FilterOption::CarriageReturnNewline | FilterOption::ControlCodes)); + VERIFY_ARE_EQUAL(L"Hello\rWorld\r 123", FilterStringForPaste(multiCRLFWithControlCodes, FilterOption::CarriageReturnNewline | FilterOption::ControlCodes)); const std::wstring multiLineWithLotsOfControlCodes = L"e\bc\bh\bo\b \b'.\b!\b:\b\b \bke\bS\b \bi3\bl \bld\bK\bo\b -1\b+\b9 +\b2\b-1'\b >\b \b/\bt\bm\bp\b/\bl\bo\bl\b\r\nsleep 1\r\nmd5sum /tmp/lol"; VERIFY_ARE_EQUAL(L"echo '.!: keS i3l ldKo -1+9 +2-1' > /tmp/lol\rsleep 1\rmd5sum /tmp/lol", - ConvertPasteString(multiLineWithLotsOfControlCodes, PasteOption::CarriageReturnNewline | PasteOption::FilterControlCodes)); - - // Test bracketed paste - VERIFY_ARE_EQUAL(L"\x1b[200~Hello World\x1b[201~", ConvertPasteString(noNewLine, PasteOption::Bracketed)); - VERIFY_ARE_EQUAL(L"\x1b[200~echo '.!: keS i3l ldKo -1+9 +2-1' > /tmp/lol\rsleep 1\rmd5sum /tmp/lol\x1b[201~", - ConvertPasteString(multiLineWithLotsOfControlCodes, PasteOption::CarriageReturnNewline | PasteOption::FilterControlCodes | PasteOption::Bracketed)); - // Malicious string that tries to prematurely terminate bracketed - const std::wstring malicious = L"echo\x1b[201~"; - VERIFY_ARE_EQUAL(L"\x1b[200~echo[201~\x1b[201~", ConvertPasteString(malicious, PasteOption::FilterControlCodes | PasteOption::Bracketed)); + FilterStringForPaste(multiLineWithLotsOfControlCodes, FilterOption::CarriageReturnNewline | FilterOption::ControlCodes)); // Test Unicode content const std::wstring unicodeString = L"你好\r\n\x01世界\x02\r\n123"; - VERIFY_ARE_EQUAL(L"\x1b[200~你好\r世界\r123\x1b[201~", - ConvertPasteString(unicodeString, PasteOption::CarriageReturnNewline | PasteOption::FilterControlCodes | PasteOption::Bracketed)); + VERIFY_ARE_EQUAL(L"你好\r世界\r123", + FilterStringForPaste(unicodeString, FilterOption::CarriageReturnNewline | FilterOption::ControlCodes)); } void UtilsTests::TestStringToUint() diff --git a/src/types/utils.cpp b/src/types/utils.cpp index ef4212aa980..9e68ee969e6 100644 --- a/src/types/utils.cpp +++ b/src/types/utils.cpp @@ -435,10 +435,10 @@ catch (...) // - option - option to use. // Return Value: // - The result string. -std::wstring Utils::ConvertPasteString(const std::wstring& wstr, const PasteOption option) +std::wstring Utils::FilterStringForPaste(const std::wstring_view wstr, const FilterOption option) { - std::wstring converted; - converted.reserve(wstr.length()); + std::wstring filtered; + filtered.reserve(wstr.length()); const auto isControlCode = [](wchar_t c) { if (c >= L'\x20' && c <= L'\x7f') @@ -449,11 +449,11 @@ std::wstring Utils::ConvertPasteString(const std::wstring& wstr, const PasteOpti if (c > L'\x7f') { - // Not a control character. + // Not a control code. return false; } - // All ASCII control characters will be removed except HT(0x09), LF(0x0a), + // All ASCII control codes will be removed except HT(0x09), LF(0x0a), // CR(0x0d) and DEL(0x7F). return c >= L'\x00' && c <= L'\x08' || c >= L'\x0b' && c <= L'\x0c' || @@ -463,33 +463,28 @@ std::wstring Utils::ConvertPasteString(const std::wstring& wstr, const PasteOpti std::wstring::size_type pos = 0; std::wstring::size_type begin = 0; - if (WI_IsFlagSet(option, PasteOption::Bracketed)) - { - converted.append(L"\x1b[200~"); - } - while (pos < wstr.size()) { const wchar_t c = wstr.at(pos); - if (WI_IsFlagSet(option, PasteOption::CarriageReturnNewline) && c == L'\n') + if (WI_IsFlagSet(option, FilterOption::CarriageReturnNewline) && c == L'\n') { // copy up to but not including the \n - converted.append(wstr.cbegin() + begin, wstr.cbegin() + pos); + filtered.append(wstr.cbegin() + begin, wstr.cbegin() + pos); if (!(pos > 0 && (wstr.at(pos - 1) == L'\r'))) { // there was no \r before the \n we did not copy, // so append our own \r (this effectively replaces the \n // with a \r) - converted.push_back(L'\r'); + filtered.push_back(L'\r'); } ++pos; begin = pos; } - else if (WI_IsFlagSet(option, PasteOption::FilterControlCodes) && isControlCode(c)) + else if (WI_IsFlagSet(option, FilterOption::ControlCodes) && isControlCode(c)) { - // copy up to but not including the control character - converted.append(wstr.cbegin() + begin, wstr.cbegin() + pos); + // copy up to but not including the control code + filtered.append(wstr.cbegin() + begin, wstr.cbegin() + pos); ++pos; begin = pos; } @@ -505,28 +500,15 @@ std::wstring Utils::ConvertPasteString(const std::wstring& wstr, const PasteOpti // and we can just write the original string if (begin == 0) { - if (WI_IsFlagSet(option, PasteOption::Bracketed)) - { - converted.append(wstr); - converted.append(L"\x1b[201~"); - return converted; - } - else - { - return wstr; - } + return std::wstring(wstr); } else { - converted.append(wstr.cbegin() + begin, wstr.cend()); - if (WI_IsFlagSet(option, PasteOption::Bracketed)) - { - converted.append(L"\x1b[201~"); - } + filtered.append(wstr.cbegin() + begin, wstr.cend()); // we may have removed some characters, so we may not need as much space // as we reserved earlier - converted.shrink_to_fit(); - return converted; + filtered.shrink_to_fit(); + return filtered; } } From f9569621887b582fb9d1be4b79da27b1a3ca9451 Mon Sep 17 00:00:00 2001 From: Chester Liu Date: Sat, 6 Feb 2021 19:01:24 +0800 Subject: [PATCH 6/8] PR feedback --- .github/actions/spelling/excludes.txt | 1 + .github/actions/spelling/expect/expect.txt | 3 --- src/cascadia/TerminalControl/TermControl.cpp | 2 ++ src/cascadia/TerminalCore/Terminal.cpp | 3 --- src/types/ut_types/UtilsTests.cpp | 8 ++++++++ src/types/utils.cpp | 14 +++++++------- 6 files changed, 18 insertions(+), 13 deletions(-) diff --git a/.github/actions/spelling/excludes.txt b/.github/actions/spelling/excludes.txt index e5f04290036..0b9616e3067 100644 --- a/.github/actions/spelling/excludes.txt +++ b/.github/actions/spelling/excludes.txt @@ -59,6 +59,7 @@ SUMS$ ^src/interactivity/onecore/BgfxEngine\. ^src/renderer/wddmcon/WddmConRenderer\. ^src/terminal/parser/ft_fuzzer/VTCommandFuzzer\.cpp$ +^src/types/ut_types/UtilsTests.cpp$ ^src/tools/U8U16Test/(?:fr|ru|zh)\.txt$ ^\.github/actions/spelling/ ^\.gitignore$ diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 337c477a46e..d166c63148c 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -149,7 +149,6 @@ bgidx Bgk BGR BGRA -bh BHID biblioscape bigobj @@ -163,7 +162,6 @@ BITOPERATION bitsavers bitset BKCOLOR -bke BKGND Bksp blog @@ -1283,7 +1281,6 @@ Loewen LOGFONT LOGFONTW logissue -lol lowercased loword lparam diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index b01bf957e8e..7f7c7928cc1 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -2038,6 +2038,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation void TermControl::_SendPastedTextToConnection(const std::wstring& wstr) { _terminal->WritePastedText(wstr); + _terminal->ClearSelection(); + _terminal->TrySnapOnInput(); } // Method Description: diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 7965e9e7620..151e09a3b1e 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -413,9 +413,6 @@ void Terminal::WritePastedText(std::wstring_view stringView) { _pfnWriteInput(filtered); } - - ClearSelection(); - TrySnapOnInput(); } // Method Description: diff --git a/src/types/ut_types/UtilsTests.cpp b/src/types/ut_types/UtilsTests.cpp index a5fdebd9076..88b8926f66b 100644 --- a/src/types/ut_types/UtilsTests.cpp +++ b/src/types/ut_types/UtilsTests.cpp @@ -200,6 +200,14 @@ void UtilsTests::TestFilterStringForPaste() VERIFY_ARE_EQUAL(L"echo '.!: keS i3l ldKo -1+9 +2-1' > /tmp/lol\rsleep 1\rmd5sum /tmp/lol", FilterStringForPaste(multiLineWithLotsOfControlCodes, FilterOption::CarriageReturnNewline | FilterOption::ControlCodes)); + // Malicious string that tries to prematurely terminate bracketed + const std::wstring malicious = L"echo\x1b[201~"; + VERIFY_ARE_EQUAL(L"echo[201~", FilterStringForPaste(malicious, FilterOption::CarriageReturnNewline | FilterOption::ControlCodes)); + + // C1 control codes + const std::wstring c1ControlCodes = L"echo\x9c"; + VERIFY_ARE_EQUAL(L"echo", FilterStringForPaste(c1ControlCodes, FilterOption::CarriageReturnNewline | FilterOption::ControlCodes)); + // Test Unicode content const std::wstring unicodeString = L"你好\r\n\x01世界\x02\r\n123"; VERIFY_ARE_EQUAL(L"你好\r世界\r123", diff --git a/src/types/utils.cpp b/src/types/utils.cpp index 9e68ee969e6..b254ad497e5 100644 --- a/src/types/utils.cpp +++ b/src/types/utils.cpp @@ -441,23 +441,23 @@ std::wstring Utils::FilterStringForPaste(const std::wstring_view wstr, const Fil filtered.reserve(wstr.length()); const auto isControlCode = [](wchar_t c) { - if (c >= L'\x20' && c <= L'\x7f') + if (c >= L'\x20' && c < L'\x7f') { - // Printable ASCII characters + DEL. + // Printable ASCII characters. return false; } - if (c > L'\x7f') + if (c > L'\x9f') { // Not a control code. return false; } - // All ASCII control codes will be removed except HT(0x09), LF(0x0a), - // CR(0x0d) and DEL(0x7F). + // All C0 & C1 control codes will be removed except HT(0x09), LF(0x0a) and CR(0x0d). return c >= L'\x00' && c <= L'\x08' || c >= L'\x0b' && c <= L'\x0c' || - c >= L'\x0e' && c <= L'\x1f'; + c >= L'\x0e' && c <= L'\x1f' || + c >= L'\x7f' && c <= L'\x9F'; }; std::wstring::size_type pos = 0; @@ -500,7 +500,7 @@ std::wstring Utils::FilterStringForPaste(const std::wstring_view wstr, const Fil // and we can just write the original string if (begin == 0) { - return std::wstring(wstr); + return std::wstring{ wstr }; } else { From e914f2bd2626411eb9d1d95d04e5bd2413e684c1 Mon Sep 17 00:00:00 2001 From: Chester Liu Date: Sun, 7 Feb 2021 11:13:15 +0800 Subject: [PATCH 7/8] Programming 101 --- src/types/utils.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/types/utils.cpp b/src/types/utils.cpp index b254ad497e5..61d5223e55f 100644 --- a/src/types/utils.cpp +++ b/src/types/utils.cpp @@ -454,10 +454,7 @@ std::wstring Utils::FilterStringForPaste(const std::wstring_view wstr, const Fil } // All C0 & C1 control codes will be removed except HT(0x09), LF(0x0a) and CR(0x0d). - return c >= L'\x00' && c <= L'\x08' || - c >= L'\x0b' && c <= L'\x0c' || - c >= L'\x0e' && c <= L'\x1f' || - c >= L'\x7f' && c <= L'\x9F'; + return c != L'\x09' && c != L'\x0a' && c != L'\x0d'; }; std::wstring::size_type pos = 0; From d2c2753edc6b22428be1cb2b9589d4b4c383bdf0 Mon Sep 17 00:00:00 2001 From: Chester Liu Date: Sun, 7 Feb 2021 11:33:23 +0800 Subject: [PATCH 8/8] til::at --- src/types/utils.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types/utils.cpp b/src/types/utils.cpp index 61d5223e55f..323263382a3 100644 --- a/src/types/utils.cpp +++ b/src/types/utils.cpp @@ -462,13 +462,13 @@ std::wstring Utils::FilterStringForPaste(const std::wstring_view wstr, const Fil while (pos < wstr.size()) { - const wchar_t c = wstr.at(pos); + const wchar_t c = til::at(wstr, pos); if (WI_IsFlagSet(option, FilterOption::CarriageReturnNewline) && c == L'\n') { // copy up to but not including the \n filtered.append(wstr.cbegin() + begin, wstr.cbegin() + pos); - if (!(pos > 0 && (wstr.at(pos - 1) == L'\r'))) + if (!(pos > 0 && (til::at(wstr, pos - 1) == L'\r'))) { // there was no \r before the \n we did not copy, // so append our own \r (this effectively replaces the \n