Repository: microsoft/terminal (language: cpp)

Issue:
WSL paths need further escape of single quotes
### Windows Terminal version

1.20.11781.0

### Windows build number

10.0.26100.0

### Other Software

Ubuntu 24.04 (in WSL and `source` of `Windows.Terminal.Wsl` in the profile; any WSL distributions are affected)

### Steps to reproduce

1. Open a WSL tab.
2. Drag and drop a file/folder (the full path needs at least one single quote `'` character), say `D:\John's Archive`.

### Expected Behavior

When
```
D:\John's Archive
```
is dropped to a WSL tab, a string which expands to:
```
/mnt/d/John's Archive
```
(single argument) in the shell should be dropped.

An example satisfying this condition includes `'/mnt/d/John'"'"'s Archive'` (escaping a single quote `'` with `'"'"'` (finish quote, print a single quote character then begin quote again).

Note: the *expected* behavior may differ depending on which environment we should assume.
*This expected behavior here* is of the POSIX shell context and I think this is reasonable.

### Actual Behavior

In the dropped path, single quotes in the original full path are kept as-is (`D:\John's Archive` → `'/mnt/d/John's Archive'`).

This is clearly wrong in the POSIX shell context because a quoted string ends before `s` and the space after `s` is not quoted and will not be preserved.


Discussion / hints:

Fix patch:
diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp
index d7bf9f496a4..bc005ba5c15 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


Test patch: