fix(pwsh): use more reliable native invocation

Rewrite logic of passing arguments in `Start-Utf8Process`:

- Use ArgumentList in PowerShell 6.1 and later.
- Use a manual escaping in lower versions.
This commit is contained in:
L. Yeung 2022-04-27 22:18:45 +08:00 committed by Jan De Dobbeleer
parent 07e0d0e0c6
commit 9606d5a105

View file

@ -20,21 +20,40 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
function Start-Utf8Process { function Start-Utf8Process {
param( param(
[string] $FileName, [string] $FileName,
[string] $Arguments [string[]] $Arguments = @()
) )
$Process = New-Object System.Diagnostics.Process $Process = New-Object System.Diagnostics.Process
$StartInfo = $Process.StartInfo $StartInfo = $Process.StartInfo
$StartInfo.FileName = $FileName
if ($StartInfo.ArgumentList.Add) {
# ArgumentList is supported in PowerShell 6.1 and later (built on .NET Core 2.1+)
# ref-1: https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.processstartinfo.argumentlist?view=net-6.0
# ref-2: https://docs.microsoft.com/en-us/powershell/scripting/whats-new/differences-from-windows-powershell?view=powershell-7.2#net-framework-vs-net-core
$Arguments | ForEach-Object -Process { $StartInfo.ArgumentList.Add($_) }
} else {
# escape arguments manually in lower versions, refer to https://docs.microsoft.com/en-us/previous-versions/17w5ykft(v=vs.85)
$escapedArgs = $Arguments | ForEach-Object {
# escape N consecutive backslash(es), which are followed by a double quote, to 2N consecutive ones
$s = $_ -replace '(\\+)"', '$1$1"'
# escape N consecutive backslash(es), which are at the end of the string, to 2N consecutive ones
$s = $s -replace '(\\+)$', '$1$1'
# escape double quotes
$s = $s -replace '"', '\"'
# quote the argument
"`"$s`""
}
$StartInfo.Arguments = $escapedArgs -join ' '
}
$StartInfo.StandardErrorEncoding = $StartInfo.StandardOutputEncoding = [System.Text.Encoding]::UTF8 $StartInfo.StandardErrorEncoding = $StartInfo.StandardOutputEncoding = [System.Text.Encoding]::UTF8
$StartInfo.RedirectStandardError = $StartInfo.RedirectStandardInput = $StartInfo.RedirectStandardOutput = $true $StartInfo.RedirectStandardError = $StartInfo.RedirectStandardInput = $StartInfo.RedirectStandardOutput = $true
$StartInfo.FileName = $Filename
$StartInfo.Arguments = $Arguments
$StartInfo.UseShellExecute = $false $StartInfo.UseShellExecute = $false
if ($PWD.Provider.Name -eq 'FileSystem') { if ($PWD.Provider.Name -eq 'FileSystem') {
$StartInfo.WorkingDirectory = $PWD.ProviderPath $StartInfo.WorkingDirectory = $PWD.ProviderPath
} }
$StartInfo.CreateNoWindow = $true $StartInfo.CreateNoWindow = $true
[void]$Process.Start() [void]$Process.Start()
# we do this to remove a deadlock potential on Windows
$stdoutTask = $Process.StandardOutput.ReadToEndAsync() $stdoutTask = $Process.StandardOutput.ReadToEndAsync()
$stderrTask = $Process.StandardError.ReadToEndAsync() $stderrTask = $Process.StandardError.ReadToEndAsync()
[void]$Process.WaitForExit() [void]$Process.WaitForExit()
@ -70,7 +89,7 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
$cursor = $null $cursor = $null
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$command, [ref]$cursor) [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$command, [ref]$cursor)
$command = ($command -split " ")[0] $command = ($command -split " ")[0]
$standardOut = @(Start-Utf8Process $script:OMPExecutable "print tooltip --pwd=""$cleanPWD"" --pswd=""$cleanPSWD"" --config=""$env:POSH_THEME"" --command=""$command"" --shell-version=""$script:PSVersion""") $standardOut = @(Start-Utf8Process $script:OMPExecutable @("print", "tooltip", "--pwd=$cleanPWD", "--pswd=$cleanPSWD", "--config=$env:POSH_THEME", "--command=$command", "--shell-version=$script:PSVersion"))
Write-Host $standardOut -NoNewline Write-Host $standardOut -NoNewline
$host.UI.RawUI.CursorPosition = $position $host.UI.RawUI.CursorPosition = $position
} }
@ -91,8 +110,8 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
} }
function Enable-PoshLineError { function Enable-PoshLineError {
$validLine = @(Start-Utf8Process $script:OMPExecutable "print valid --config=""$env:POSH_THEME""") -join "`n" $validLine = @(Start-Utf8Process $script:OMPExecutable @("print", "valid", "--config=$env:POSH_THEME")) -join "`n"
$errorLine = @(Start-Utf8Process $script:OMPExecutable "print error --config=""$env:POSH_THEME""") -join "`n" $errorLine = @(Start-Utf8Process $script:OMPExecutable @("print", "error", "--config=$env:POSH_THEME")) -join "`n"
Set-PSReadLineOption -PromptText $validLine, $errorLine Set-PSReadLineOption -PromptText $validLine, $errorLine
} }
@ -131,7 +150,7 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
$Format = 'json' $Format = 'json'
) )
$configString = @(Start-Utf8Process $script:OMPExecutable "config export --config=""$env:POSH_THEME"" --format=$Format") $configString = @(Start-Utf8Process $script:OMPExecutable @("config", "export", "--config=$env:POSH_THEME", "--format=$Format"))
# if no path, copy to clipboard by default # if no path, copy to clipboard by default
if ('' -ne $FilePath) { if ('' -ne $FilePath) {
# https://stackoverflow.com/questions/3038337/powershell-resolve-path-that-might-not-exist # https://stackoverflow.com/questions/3038337/powershell-resolve-path-that-might-not-exist
@ -198,7 +217,7 @@ New-Module -Name "oh-my-posh-core" -ScriptBlock {
} else { } else {
$themes | ForEach-Object -Process { $themes | ForEach-Object -Process {
Write-Host "Theme: $(Get-FileHyperlink -uri $_.FullName -Name ($_.BaseName -replace '\.omp$', ''))`n" Write-Host "Theme: $(Get-FileHyperlink -uri $_.FullName -Name ($_.BaseName -replace '\.omp$', ''))`n"
@(Start-Utf8Process $script:OMPExecutable "print primary --config=""$($_.FullName)"" --pwd=""$PWD"" --shell pwsh") @(Start-Utf8Process $script:OMPExecutable @("print", "primary", "--config=$($_.FullName)", "--pwd=$PWD", "--shell=pwsh"))
Write-Host "`n" Write-Host "`n"
} }
} }
@ -220,12 +239,12 @@ Example:
$realLASTEXITCODE = $global:LASTEXITCODE $realLASTEXITCODE = $global:LASTEXITCODE
$cleanPWD, $cleanPSWD = Get-PoshContext $cleanPWD, $cleanPSWD = Get-PoshContext
if ($script:TransientPrompt -eq $true) { if ($script:TransientPrompt -eq $true) {
@(Start-Utf8Process $script:OMPExecutable "print transient --error=$script:ErrorCode --pwd=""$cleanPWD"" --pswd=""$cleanPSWD"" --execution-time=$script:ExecutionTime --config=""$env:POSH_THEME"" --shell-version=""$script:PSVersion""") -join "`n" @(Start-Utf8Process $script:OMPExecutable @("print", "transient", "--error=$script:ErrorCode", "--pwd=$cleanPWD", "--pswd=$cleanPSWD", "--execution-time=$script:ExecutionTime", "--config=$env:POSH_THEME", "--shell-version=$script:PSVersion")) -join "`n"
$script:TransientPrompt = $false $script:TransientPrompt = $false
return return
} }
if (Test-Path variable:/PSDebugContext) { if (Test-Path variable:/PSDebugContext) {
@(Start-Utf8Process $script:OMPExecutable "print debug --pwd=""$cleanPWD"" --pswd=""$cleanPSWD"" --config=""$env:POSH_THEME""") -join "`n" @(Start-Utf8Process $script:OMPExecutable @("print", "debug", "--pwd=$cleanPWD", "--pswd=$cleanPSWD", "--config=$env:POSH_THEME")) -join "`n"
return return
} }
Initialize-ModuleSupport Initialize-ModuleSupport
@ -261,7 +280,7 @@ Example:
Set-PoshContext Set-PoshContext
$terminalWidth = $Host.UI.RawUI.WindowSize.Width $terminalWidth = $Host.UI.RawUI.WindowSize.Width
$standardOut = @(Start-Utf8Process $script:OMPExecutable "print primary --error=$script:ErrorCode --pwd=""$cleanPWD"" --pswd=""$cleanPSWD"" --execution-time=$script:ExecutionTime --stack-count=$stackCount --config=""$env:POSH_THEME"" --shell-version=""$script:PSVersion"" --terminal-width=$terminalWidth") $standardOut = @(Start-Utf8Process $script:OMPExecutable @("print", "primary", "--error=$script:ErrorCode", "--pwd=$cleanPWD", "--pswd=$cleanPSWD", "--execution-time=$script:ExecutionTime", "--stack-count=$stackCount", "--config=$env:POSH_THEME", "--shell-version=$script:PSVersion", "--terminal-width=$terminalWidth"))
# make sure PSReadLine knows we have a multiline prompt # make sure PSReadLine knows we have a multiline prompt
$extraLines = ($standardOut | Measure-Object -Line).Lines - 1 $extraLines = ($standardOut | Measure-Object -Line).Lines - 1
if ($extraLines -gt 0) { if ($extraLines -gt 0) {
@ -273,7 +292,7 @@ Example:
} }
# set secondary prompt # set secondary prompt
Set-PSReadLineOption -ContinuationPrompt (@(Start-Utf8Process $script:OMPExecutable "print secondary --config=""$env:POSH_THEME""") -join "`n") Set-PSReadLineOption -ContinuationPrompt (@(Start-Utf8Process $script:OMPExecutable @("print", "secondary", "--config=$env:POSH_THEME")) -join "`n")
Export-ModuleMember -Function @( Export-ModuleMember -Function @(
"Set-PoshContext" "Set-PoshContext"