Move this function to types/ so we can write tests for it

This commit is contained in:
Mike Griese
2022-01-06 09:29:39 -06:00
parent 805ac4c594
commit e64ae7d04b
4 changed files with 198 additions and 67 deletions

View File

@@ -61,72 +61,6 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
return S_OK; 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<std::wstring, std::wstring> _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: // Function Description:
// - launches the client application attached to the new pseudoconsole // - launches the client application attached to the new pseudoconsole
HRESULT ConptyConnection::_LaunchAttachedClient() noexcept HRESULT ConptyConnection::_LaunchAttachedClient() noexcept
@@ -233,7 +167,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
siEx.StartupInfo.lpTitle = mutableTitle.data(); siEx.StartupInfo.lpTitle = mutableTitle.data();
} }
auto [newCommandLine, newStartingDirectory] = _tryMangleStartingDirectoryForWSL(cmdline, _startingDirectory); auto [newCommandLine, newStartingDirectory] = Utils::MangleStartingDirectoryForWSL(cmdline, _startingDirectory);
const wchar_t* const startingDirectory = newStartingDirectory.size() > 0 ? newStartingDirectory.c_str() : nullptr; const wchar_t* const startingDirectory = newStartingDirectory.size() > 0 ? newStartingDirectory.c_str() : nullptr;
RETURN_IF_WIN32_BOOL_FALSE(CreateProcessW( RETURN_IF_WIN32_BOOL_FALSE(CreateProcessW(

View File

@@ -95,4 +95,13 @@ namespace Microsoft::Console::Utils
GUID CreateV5Uuid(const GUID& namespaceGuid, const gsl::span<const gsl::byte> name); GUID CreateV5Uuid(const GUID& namespaceGuid, const gsl::span<const gsl::byte> name);
bool IsElevated(); bool IsElevated();
// This function is only ever used by the ConPTY connection in
// TerminalConnection. However, that library does not have a good systen of
// tests set up. Since this function has a plethora of edge cases that would
// be beneficial to have tests for, we're hosting it in this lib, so it can
// be easily tested.
std::tuple<std::wstring, std::wstring> MangleStartingDirectoryForWSL(std::wstring_view commandLine,
std::wstring_view startingDirectory);
} }

View File

@@ -26,6 +26,8 @@ class UtilsTests
TEST_METHOD(TestStringToUint); TEST_METHOD(TestStringToUint);
TEST_METHOD(TestColorFromXTermColor); TEST_METHOD(TestColorFromXTermColor);
TEST_METHOD(TestMangleWSLPaths);
void _VerifyXTermColorResult(const std::wstring_view wstr, DWORD colorValue); void _VerifyXTermColorResult(const std::wstring_view wstr, DWORD colorValue);
void _VerifyXTermColorInvalid(const std::wstring_view wstr); void _VerifyXTermColorInvalid(const std::wstring_view wstr);
}; };
@@ -332,3 +334,120 @@ void UtilsTests::_VerifyXTermColorInvalid(const std::wstring_view wstr)
std::optional<til::color> color = ColorFromXTermColor(wstr); std::optional<til::color> color = ColorFromXTermColor(wstr);
VERIFY_IS_FALSE(color.has_value()); VERIFY_IS_FALSE(color.has_value());
} }
void UtilsTests::TestMangleWSLPaths()
{
// Continue on failures
const WEX::TestExecution::DisableVerifyExceptions disableExceptionsScope;
const auto startingDirectory{ L"SENTINEL" };
// MUST MANGLE
{
auto [commandline, path] = MangleStartingDirectoryForWSL(LR"(wsl)", startingDirectory);
VERIFY_ARE_EQUAL(LR"("wsl" --cd "SENTINEL" )", commandline);
VERIFY_ARE_EQUAL(L"", path);
}
{
auto [commandline, path] = MangleStartingDirectoryForWSL(LR"(wsl -d X)", startingDirectory);
VERIFY_ARE_EQUAL(LR"("wsl" --cd "SENTINEL" -d X)", commandline);
VERIFY_ARE_EQUAL(L"", path);
}
{
auto [commandline, path] = MangleStartingDirectoryForWSL(LR"(wsl -d X ~/bin/sh)", startingDirectory);
VERIFY_ARE_EQUAL(LR"("wsl" --cd "SENTINEL" -d X ~/bin/sh)", commandline);
VERIFY_ARE_EQUAL(L"", path);
}
{
auto [commandline, path] = MangleStartingDirectoryForWSL(LR"(wsl.exe)", startingDirectory);
VERIFY_ARE_EQUAL(LR"("wsl.exe" --cd "SENTINEL" )", commandline);
VERIFY_ARE_EQUAL(L"", path);
}
{
auto [commandline, path] = MangleStartingDirectoryForWSL(LR"(wsl.exe -d X)", startingDirectory);
VERIFY_ARE_EQUAL(LR"("wsl.exe" --cd "SENTINEL" -d X)", commandline);
VERIFY_ARE_EQUAL(L"", path);
}
{
auto [commandline, path] = MangleStartingDirectoryForWSL(LR"(wsl.exe -d X ~/bin/sh)", startingDirectory);
VERIFY_ARE_EQUAL(LR"("wsl.exe" --cd "SENTINEL" -d X ~/bin/sh)", commandline);
VERIFY_ARE_EQUAL(L"", path);
}
{
auto [commandline, path] = MangleStartingDirectoryForWSL(LR"("wsl")", startingDirectory);
VERIFY_ARE_EQUAL(LR"("wsl" --cd "SENTINEL" )", commandline);
VERIFY_ARE_EQUAL(L"", path);
}
{
auto [commandline, path] = MangleStartingDirectoryForWSL(LR"("wsl.exe")", startingDirectory);
VERIFY_ARE_EQUAL(LR"("wsl.exe" --cd "SENTINEL" )", commandline);
VERIFY_ARE_EQUAL(L"", path);
}
{
auto [commandline, path] = MangleStartingDirectoryForWSL(LR"("wsl" -d X)", startingDirectory);
VERIFY_ARE_EQUAL(LR"("wsl" --cd "SENTINEL" -d X)", commandline);
VERIFY_ARE_EQUAL(L"", path);
}
{
auto [commandline, path] = MangleStartingDirectoryForWSL(LR"("wsl.exe" -d X)", startingDirectory);
VERIFY_ARE_EQUAL(LR"("wsl.exe" --cd "SENTINEL" -d X)", commandline);
VERIFY_ARE_EQUAL(L"", path);
}
{
auto [commandline, path] = MangleStartingDirectoryForWSL(LR"("C:\Windows\system32\wsl.exe" -d X)", startingDirectory);
VERIFY_ARE_EQUAL(LR"("C:\Windows\system32\wsl.exe" --cd "SENTINEL" -d X)", commandline);
VERIFY_ARE_EQUAL(L"", path);
}
{
auto [commandline, path] = MangleStartingDirectoryForWSL(LR"("C:\windows\system32\wsl" -d X)", startingDirectory);
VERIFY_ARE_EQUAL(LR"("C:\windows\system32\wsl" --cd "SENTINEL" -d X)", commandline);
VERIFY_ARE_EQUAL(L"", path);
}
{
auto [commandline, path] = MangleStartingDirectoryForWSL(LR"(wsl ~/bin)", startingDirectory);
VERIFY_ARE_EQUAL(LR"("wsl" --cd "SENTINEL" ~/bin)", commandline);
VERIFY_ARE_EQUAL(L"", path);
}
// MUST NOT MANGLE
{
auto [commandline, path] = MangleStartingDirectoryForWSL(LR"("C:\wsl.exe" -d X)", startingDirectory);
VERIFY_ARE_EQUAL(LR"("C:\wsl.exe" -d X)", commandline);
VERIFY_ARE_EQUAL(startingDirectory, path);
}
{
auto [commandline, path] = MangleStartingDirectoryForWSL(LR"(C:\wsl.exe)", startingDirectory);
VERIFY_ARE_EQUAL(LR"(C:\wsl.exe)", commandline);
VERIFY_ARE_EQUAL(startingDirectory, path);
}
{
auto [commandline, path] = MangleStartingDirectoryForWSL(LR"(wsl --cd C:\)", startingDirectory);
VERIFY_ARE_EQUAL(LR"(wsl --cd C:\)", commandline);
VERIFY_ARE_EQUAL(startingDirectory, path);
}
{
auto [commandline, path] = MangleStartingDirectoryForWSL(LR"(wsl ~)", startingDirectory);
VERIFY_ARE_EQUAL(LR"(wsl ~)", commandline);
VERIFY_ARE_EQUAL(startingDirectory, path);
}
{
auto [commandline, path] = MangleStartingDirectoryForWSL(LR"(wsl ~ -d Ubuntu)", startingDirectory);
VERIFY_ARE_EQUAL(LR"(wsl ~ -d Ubuntu)", commandline);
VERIFY_ARE_EQUAL(startingDirectory, path);
}
}

View File

@@ -6,6 +6,7 @@
#include "inc/colorTable.hpp" #include "inc/colorTable.hpp"
#include <wil/token_helpers.h> #include <wil/token_helpers.h>
#include <til/string.h>
using namespace Microsoft::Console; using namespace Microsoft::Console;
@@ -591,3 +592,71 @@ bool Utils::IsElevated()
}(); }();
return isElevated; return isElevated;
} }
// 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.
std::tuple<std::wstring, std::wstring> Utils::MangleStartingDirectoryForWSL(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 (!til::equals_insensitive_ascii(executablePath.parent_path().c_str(), 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 }
};
}