diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 09cc175d693..653ae691594 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -837,13 +837,19 @@ namespace winrt::TerminalApp::implementation // construction, because the connection might not spawn the child // process until later, on another thread, after we've already // restored the CWD to it's original value. - std::wstring cwdString{ wil::GetCurrentDirectoryW() }; - std::filesystem::path cwd{ cwdString }; - cwd /= settings.StartingDirectory().c_str(); + winrt::hstring newWorkingDirectory{ settings.StartingDirectory() }; + if (newWorkingDirectory.size() <= 1 || + !(newWorkingDirectory[0] == L'~' || newWorkingDirectory[0] == L'/')) + { // We only want to resolve the new WD against the CWD if it doesn't look like a Linux path (see GH#592) + std::wstring cwdString{ wil::GetCurrentDirectoryW() }; + std::filesystem::path cwd{ cwdString }; + cwd /= settings.StartingDirectory().c_str(); + newWorkingDirectory = winrt::hstring{ cwd.wstring() }; + } auto conhostConn = TerminalConnection::ConptyConnection(); conhostConn.Initialize(TerminalConnection::ConptyConnection::CreateSettings(settings.Commandline(), - winrt::hstring{ cwd.wstring() }, + newWorkingDirectory, settings.StartingTitle(), envMap.GetView(), ::base::saturated_cast(settings.InitialRows()), diff --git a/src/cascadia/TerminalConnection/ConptyConnection.cpp b/src/cascadia/TerminalConnection/ConptyConnection.cpp index 1589f708d55..d82b6be0a4f 100644 --- a/src/cascadia/TerminalConnection/ConptyConnection.cpp +++ b/src/cascadia/TerminalConnection/ConptyConnection.cpp @@ -57,6 +57,72 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation return S_OK; } + // Function Description: + // - Promotes a starting directory provided to a WSL invocation to a commandline argument. + // This is necessary because WSL has some modicum of support for linux-side directories (!) which + // CreateProcess never will. + static std::tuple _tryMangleStartingDirectoryForWSL(std::wstring_view commandLine, std::wstring_view startingDirectory) + { + do + { + if (startingDirectory.size() > 0 && commandLine.size() >= 3) + { // "wsl" is three characters; this is a safe bet. no point in doing it if there's no starting directory though! + // Find the first space, quote or the end of the string -- we'll look for wsl before that. + const auto terminator{ commandLine.find_first_of(LR"(" )", 1) }; // look past the first character in case it starts with " + const auto start{ til::at(commandLine, 0) == L'"' ? 1 : 0 }; + const std::filesystem::path executablePath{ commandLine.substr(start, terminator - start) }; + const auto executableFilename{ executablePath.filename().wstring() }; + if (executableFilename == L"wsl" || executableFilename == L"wsl.exe") + { + // We've got a WSL -- let's just make sure it's the right one. + if (executablePath.has_parent_path()) + { + std::wstring systemDirectory{}; + if (FAILED(wil::GetSystemDirectoryW(systemDirectory))) + { + break; // just bail out. + } + if (executablePath.parent_path().wstring() != systemDirectory) + { + break; // it wasn't in system32! + } + } + else + { + // assume that unqualified WSL is the one in system32 (minor danger) + } + + const auto arguments{ terminator == std::wstring_view::npos ? std::wstring_view{} : commandLine.substr(terminator + 1) }; + if (arguments.find(L"--cd") != std::wstring_view::npos) + { + break; // they've already got a --cd! + } + + const auto tilde{ arguments.find_first_of(L'~') }; + if (tilde != std::wstring_view::npos) + { + if (tilde + 1 == arguments.size() || til::at(arguments, tilde + 1) == L' ') + { + // We want to suppress --cd if they have added a bare ~ to their commandline (they conflict). + break; + } + // Tilde followed by non-space should be okay (like, wsl -d Debian ~/blah.sh) + } + + return { + fmt::format(LR"("{}" --cd "{}" {})", executablePath.wstring(), startingDirectory, arguments), + std::wstring{} + }; + } + } + } while (false); + + return { + std::wstring{ commandLine }, + std::wstring{ startingDirectory } + }; + } + // Function Description: // - launches the client application attached to the new pseudoconsole HRESULT ConptyConnection::_LaunchAttachedClient() noexcept @@ -163,11 +229,12 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation siEx.StartupInfo.lpTitle = mutableTitle.data(); } - const wchar_t* const startingDirectory = _startingDirectory.size() > 0 ? _startingDirectory.c_str() : nullptr; + auto [newCommandLine, newStartingDirectory] = _tryMangleStartingDirectoryForWSL(cmdline, _startingDirectory); + const wchar_t* const startingDirectory = newStartingDirectory.size() > 0 ? newStartingDirectory.c_str() : nullptr; RETURN_IF_WIN32_BOOL_FALSE(CreateProcessW( nullptr, - cmdline.data(), + newCommandLine.data(), nullptr, // lpProcessAttributes nullptr, // lpThreadAttributes false, // bInheritHandles diff --git a/src/cascadia/TerminalSettingsModel/Profile.cpp b/src/cascadia/TerminalSettingsModel/Profile.cpp index a684acc95ab..d90a65884d4 100644 --- a/src/cascadia/TerminalSettingsModel/Profile.cpp +++ b/src/cascadia/TerminalSettingsModel/Profile.cpp @@ -448,11 +448,6 @@ winrt::Microsoft::Terminal::Settings::Model::FontConfig Profile::FontInfo() // - the function returns an evaluated version of %userprofile% to avoid blocking the session from starting. std::wstring Profile::EvaluateStartingDirectory(const std::wstring& directory) { - // First expand path - DWORD numCharsInput = ExpandEnvironmentStrings(directory.c_str(), nullptr, 0); - std::unique_ptr evaluatedPath = std::make_unique(numCharsInput); - THROW_LAST_ERROR_IF(0 == ExpandEnvironmentStrings(directory.c_str(), evaluatedPath.get(), numCharsInput)); - // Prior to GH#9541, we'd validate that the user's startingDirectory existed // here. If it was invalid, we'd gracefully fall back to %USERPROFILE%. // @@ -463,7 +458,7 @@ std::wstring Profile::EvaluateStartingDirectory(const std::wstring& directory) // // If the path is eventually invalid, we'll display warning in the // ConptyConnection when the process fails to launch. - return std::wstring(evaluatedPath.get(), numCharsInput); + return wil::ExpandEnvironmentStringsW(directory.c_str()); } // Function Description: