From ae90d52bb18fc7ad1d509aa81c7df71ada0e3b1f Mon Sep 17 00:00:00 2001
From: Tsukasa OI
Date: Sun, 24 Nov 2024 03:34:21 +0900
Subject: [PATCH] Escape single quotes while translating dropped Win32 paths
(#18007)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
When file/folder is dropped to the terminal, its path is translated and
quoted with a pair of single quotes if necessary.
However, the terminal control does not escape single quotes (allowed in
the Win32 subsystem) that need escapes when translated.
On the translation styles other than `"none"` (note: all other
translation styles are currently intended for the POSIX shell), it
causes incorrect path to be pasted when the path contains one or more
single quotes (see #18006 for an example).
With this commit, the terminal control escapes a single quote with a
valid escape sequence `'\''` (finish quote, print a single quote then
begin quote again) when the path translation is required.
## History
### v1 → v2
* Changed escape sequence from `'"'"'` to much shorter `'\''`.
* Reflected comments by the reviewer.
### v2 → v3
* Overhaul after addition of multiple path translation styles (not just
WSL but Cygwin and MSYS).
* More clarification both in the code and in the commit message.
### v3 → v4 (current)
* Minor clarification both in the code and in the commit message.
## References and Relevant Issues
* #18006
* #16214
* #18195
## Detailed Description of the Pull Request / Additional comments
This is a follow-up of #16214 and #18195, fixing #18006.
Closes #18006
---
src/cascadia/TerminalControl/TermControl.cpp | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp
index d7bf9f496a..bc005ba5c1 100644
--- a/src/cascadia/TerminalControl/TermControl.cpp
+++ b/src/cascadia/TerminalControl/TermControl.cpp
@@ -66,6 +66,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
/* Cygwin */ L"/cygdrive/",
/* MSYS2 */ L"/",
};
+ static constexpr wil::zwstring_view sSingleQuoteEscape = L"'\\''";
+ static constexpr auto cchSingleQuoteEscape = sSingleQuoteEscape.size();
if (translationStyle == PathTranslationStyle::None)
{
@@ -75,6 +77,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// All of the other path translation modes current result in /-delimited paths
std::replace(fullPath.begin(), fullPath.end(), L'\\', L'/');
+ // Escape single quotes, assuming translated paths are always quoted by a pair of single quotes.
+ size_t pos = 0;
+ while ((pos = fullPath.find(L'\'', pos)) != std::wstring::npos)
+ {
+ // ' -> '\'' (for POSIX shell)
+ fullPath.replace(pos, 1, sSingleQuoteEscape);
+ // Arithmetic overflow cannot occur here.
+ pos += cchSingleQuoteEscape;
+ }
+
if (fullPath.size() >= 2 && fullPath.at(1) == L':')
{
// C:/foo/bar -> Cc/foo/bar