From f26054fe5585c8b0d89cb2e70eac647d6b6fe3c6 Mon Sep 17 00:00:00 2001 From: Keith Hill Date: Sat, 2 Mar 2019 20:53:52 -0700 Subject: [PATCH 1/6] Pipe help system output through Out-String -Width when not using more This fixes #7175 by using Out-String to properly wrap text before sending to less or the user specified PAGER. --- .../engine/InitialSessionState.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/System.Management.Automation/engine/InitialSessionState.cs b/src/System.Management.Automation/engine/InitialSessionState.cs index 852c7e88152..1d91f06efc9 100644 --- a/src/System.Management.Automation/engine/InitialSessionState.cs +++ b/src/System.Management.Automation/engine/InitialSessionState.cs @@ -4205,11 +4205,11 @@ .FORWARDHELPCATEGORY Cmdlet # Respect PAGER, use more on Windows, and use less on Linux if ($customPagerCommand) { - $help | & $customPagerCommand $customPagerCommandArgs + $help | Out-String -Width ([System.Console]::WindowWidth - 1) | & $customPagerCommand $customPagerCommandArgs } elseif ($IsWindows) { $help | more.com } else { - $help | less -Ps""Page %db?B of %D:.\. Press h for help or q to quit\.$"" + $help | Out-String -Width ([System.Console]::WindowWidth - 1) | less -Ps""Page %db?B of %D:.\. Press h for help or q to quit\.$"" } } "; From 1f06d43a76ada6d623a191532104a318790e3391 Mon Sep 17 00:00:00 2001 From: Keith Hill Date: Sun, 3 Mar 2019 00:19:21 -0700 Subject: [PATCH 2/6] Set the string output width when sending help to pager app Fix #7175 --- .../engine/InitialSessionState.cs | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/src/System.Management.Automation/engine/InitialSessionState.cs b/src/System.Management.Automation/engine/InitialSessionState.cs index 1d91f06efc9..c7f1556db98 100644 --- a/src/System.Management.Automation/engine/InitialSessionState.cs +++ b/src/System.Management.Automation/engine/InitialSessionState.cs @@ -4176,10 +4176,17 @@ .FORWARDHELPCATEGORY Cmdlet } elseif ($help -ne $null) { - $customPagerCommand = """" - $customPagerCommandArgs = """" - $customPagerCommandLine = """" + # Use more on Windows, and use less on Linux by default + if ($IsWindows) { + $pagerCommand = 'more.com' + $pagerArgs = $null + } + else { + $pagerCommand = 'less' + $pagerArgs = '-Ps""Page %db?B of %D:.\. Press h for help or q to quit\.$""' + } + # Respect PAGER environment variable which allows user to specify a custom pager if ($env:PAGER) { $customPagerCommandLine = $env:PAGER @@ -4193,23 +4200,25 @@ .FORWARDHELPCATEGORY Cmdlet if (-not $cmds) { # Custom command is invalid; ignore it, but issue a warning. Write-Warning ""Ignoring invalid custom-paging utility command line specified in `$env:PAGER: $customPagerCommandLine"" - $customPagerCommand = $null # use default command } elseif ($cmds.Count -eq 1 -and $cmds[0].Source -eq $customPagerCommandLine) { - # The full command line is an unquoted path to an existing executable - # with embedded spaces. - $customPagerCommand = $customPagerCommandLine - $customPagerCommandArgs = $null + # The full command line is an unquoted path to an existing executable with embedded spaces. + $pagerCommand = $customPagerCommandLine + $pagerArgs = $null + } + else { + $pagerCommand = $customPagerCommand + $pagerArgs = $customPagerCommandArgs } } - # Respect PAGER, use more on Windows, and use less on Linux - if ($customPagerCommand) { - $help | Out-String -Width ([System.Console]::WindowWidth - 1) | & $customPagerCommand $customPagerCommandArgs - } elseif ($IsWindows) { - $help | more.com - } else { - $help | Out-String -Width ([System.Console]::WindowWidth - 1) | less -Ps""Page %db?B of %D:.\. Press h for help or q to quit\.$"" + if ((Get-Command $pagerCommand -ErrorAction Ignore).CommandType -eq 'Application') { + # If the pager is an application, format the output width before sending to the app. + $help | Out-String -Stream -Width ([System.Console]::WindowWidth - 1) | & $pagerCommand $pagerArgs + } + else { + # If the pager is a PowerShell function or script, pipe directly into it. + $help | & $pagerCommand $pagerArgs } } "; From 790d2e3d2aba952b1ae23dfb943cf33967e9276e Mon Sep 17 00:00:00 2001 From: Keith Hill Date: Sun, 3 Mar 2019 01:10:49 -0700 Subject: [PATCH 3/6] Fix #8912 by using PS tokenize to get custom pager cmd & args --- .../engine/InitialSessionState.cs | 61 +++++++++++-------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/src/System.Management.Automation/engine/InitialSessionState.cs b/src/System.Management.Automation/engine/InitialSessionState.cs index c7f1556db98..28b1688b42f 100644 --- a/src/System.Management.Automation/engine/InitialSessionState.cs +++ b/src/System.Management.Automation/engine/InitialSessionState.cs @@ -4176,45 +4176,58 @@ .FORWARDHELPCATEGORY Cmdlet } elseif ($help -ne $null) { - # Use more on Windows, and use less on Linux by default + $pagerArgs = $null + + # By default use more on Windows and less on Linux. if ($IsWindows) { $pagerCommand = 'more.com' - $pagerArgs = $null } else { $pagerCommand = 'less' $pagerArgs = '-Ps""Page %db?B of %D:.\. Press h for help or q to quit\.$""' } - # Respect PAGER environment variable which allows user to specify a custom pager - if ($env:PAGER) - { - $customPagerCommandLine = $env:PAGER - - # Split the command line into tokens, respecting quoting. - $customPagerCommand, $customPagerCommandArgs = & { Write-Output -- $customPagerCommandLine } - - # See if the first token refers to a known command (executable), - # and if not, see if the command line as a whole is an executable path. - $cmds = Get-Command $customPagerCommand, $customPagerCommandLine -ErrorAction Ignore - if (-not $cmds) { - # Custom command is invalid; ignore it, but issue a warning. - Write-Warning ""Ignoring invalid custom-paging utility command line specified in `$env:PAGER: $customPagerCommandLine"" - } - elseif ($cmds.Count -eq 1 -and $cmds[0].Source -eq $customPagerCommandLine) { - # The full command line is an unquoted path to an existing executable with embedded spaces. - $pagerCommand = $customPagerCommandLine - $pagerArgs = $null + # Respect PAGER environment variable which allows user to specify a custom pager. + # Ignore a pure whitespace PAGER value as that would cause the tokenizer to return 0 tokens. + if (![string]::IsNullOrWhitespace($env:PAGER)) { + if (Get-Command $env:PAGER -ErrorAction Ignore) { + # Entire PAGER value corresponds to a single command. + $pagerCommand = $env:PAGER } else { - $pagerCommand = $customPagerCommand - $pagerArgs = $customPagerCommandArgs + # PAGER value is not a valid command, check if PAGER command and arguments have been specified. + # Tokenize the specified $env:PAGER value. Ignore tokenizing errors since any errors may be valid + # argument syntax for the paging utility. + $errs = $null + $tokens = [System.Management.Automation.PSParser]::Tokenize($env:PAGER, [ref]$errs) + + $customPagerCommand = $tokens[0].Content + if (!(Get-Command $customPagerCommand -ErrorAction Ignore)) { + # Custom pager command is invalid, issue a warning. + Write-Warning ""Ignoring invalid custom-paging utility command line specified in `$env:PAGER: $env:PAGER"" + } + elseif ($tokens.Count -eq 1) { + $pagerCommand = $customPagerCommand + } + else { + # This approach will preserve all the pagers args. + $pagerCommand = $customPagerCommand + $pagerArgs = $env:PAGER.Substring($tokens[1].Start) + } } } if ((Get-Command $pagerCommand -ErrorAction Ignore).CommandType -eq 'Application') { # If the pager is an application, format the output width before sending to the app. - $help | Out-String -Stream -Width ([System.Console]::WindowWidth - 1) | & $pagerCommand $pagerArgs + if ($pagerArgs) { + # Supply pager arguments to an application without any PowerShell parsing of the arguments. + $env:_PSPAGER_ARGS = $pagerArgs + $help | Out-String -Stream -Width ([System.Console]::WindowWidth - 1) | & $pagerCommand --% %_PSPAGER_ARGS% + Remove-Item Env:\_PSPAGER_ARGS -ErrorAction Ignore + } + else { + $help | Out-String -Stream -Width ([System.Console]::WindowWidth - 1) | & $pagerCommand + } } else { # If the pager is a PowerShell function or script, pipe directly into it. From 9374c796688f8c2bf725577535d405aa69d505f6 Mon Sep 17 00:00:00 2001 From: Keith Hill Date: Sun, 3 Mar 2019 13:57:00 -0700 Subject: [PATCH 4/6] Fix macOS/Linux test failures --- .../engine/InitialSessionState.cs | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/System.Management.Automation/engine/InitialSessionState.cs b/src/System.Management.Automation/engine/InitialSessionState.cs index 28b1688b42f..72ad1591425 100644 --- a/src/System.Management.Automation/engine/InitialSessionState.cs +++ b/src/System.Management.Automation/engine/InitialSessionState.cs @@ -4176,11 +4176,10 @@ .FORWARDHELPCATEGORY Cmdlet } elseif ($help -ne $null) { - $pagerArgs = $null - # By default use more on Windows and less on Linux. if ($IsWindows) { $pagerCommand = 'more.com' + $pagerArgs = $null } else { $pagerCommand = 'less' @@ -4193,6 +4192,7 @@ .FORWARDHELPCATEGORY Cmdlet if (Get-Command $env:PAGER -ErrorAction Ignore) { # Entire PAGER value corresponds to a single command. $pagerCommand = $env:PAGER + $pagerArgs = $null } else { # PAGER value is not a valid command, check if PAGER command and arguments have been specified. @@ -4206,31 +4206,30 @@ .FORWARDHELPCATEGORY Cmdlet # Custom pager command is invalid, issue a warning. Write-Warning ""Ignoring invalid custom-paging utility command line specified in `$env:PAGER: $env:PAGER"" } - elseif ($tokens.Count -eq 1) { - $pagerCommand = $customPagerCommand - } else { # This approach will preserve all the pagers args. $pagerCommand = $customPagerCommand - $pagerArgs = $env:PAGER.Substring($tokens[1].Start) + $pagerArgs = if ($tokens.Count -gt 1) {$env:PAGER.Substring($tokens[1].Start)} else {$null} } } } + # If the pager is an application, format the output width before sending to the app. if ((Get-Command $pagerCommand -ErrorAction Ignore).CommandType -eq 'Application') { - # If the pager is an application, format the output width before sending to the app. + $consoleWidth = [System.Console]::WindowWidth + if ($pagerArgs) { # Supply pager arguments to an application without any PowerShell parsing of the arguments. - $env:_PSPAGER_ARGS = $pagerArgs - $help | Out-String -Stream -Width ([System.Console]::WindowWidth - 1) | & $pagerCommand --% %_PSPAGER_ARGS% - Remove-Item Env:\_PSPAGER_ARGS -ErrorAction Ignore + # Leave environment variable to help user debug arguments supplied in $env:PAGER. + $env:__PSPAGER_ARGS = $pagerArgs + $help | Out-String -Stream -Width ($consoleWidth - 1) | & $pagerCommand --% %__PSPAGER_ARGS% } else { - $help | Out-String -Stream -Width ([System.Console]::WindowWidth - 1) | & $pagerCommand + $help | Out-String -Stream -Width ($consoleWidth - 1) | & $pagerCommand } } else { - # If the pager is a PowerShell function or script, pipe directly into it. + # The pager command is a PowerShell function, script or alias, so pipe directly into it. $help | & $pagerCommand $pagerArgs } } From 9ecd62ce0169b352638e1925bafcb0643dd98b4e Mon Sep 17 00:00:00 2001 From: Keith Hill Date: Sun, 3 Mar 2019 14:59:16 -0700 Subject: [PATCH 5/6] It appears that while running in tests, ConsoleWidth is 0, set min val --- src/System.Management.Automation/engine/InitialSessionState.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Management.Automation/engine/InitialSessionState.cs b/src/System.Management.Automation/engine/InitialSessionState.cs index 72ad1591425..8aa6c65f098 100644 --- a/src/System.Management.Automation/engine/InitialSessionState.cs +++ b/src/System.Management.Automation/engine/InitialSessionState.cs @@ -4216,7 +4216,7 @@ .FORWARDHELPCATEGORY Cmdlet # If the pager is an application, format the output width before sending to the app. if ((Get-Command $pagerCommand -ErrorAction Ignore).CommandType -eq 'Application') { - $consoleWidth = [System.Console]::WindowWidth + $consoleWidth = [System.Math]::Max([System.Console]::WindowWidth, 20) if ($pagerArgs) { # Supply pager arguments to an application without any PowerShell parsing of the arguments. From bb62a371f9324ff971a6c571888b4d88d694e7c0 Mon Sep 17 00:00:00 2001 From: Keith Hill Date: Tue, 9 Apr 2019 22:15:10 -0600 Subject: [PATCH 6/6] Address PR feedback --- src/System.Management.Automation/engine/InitialSessionState.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Management.Automation/engine/InitialSessionState.cs b/src/System.Management.Automation/engine/InitialSessionState.cs index 8aa6c65f098..a578d21e1ba 100644 --- a/src/System.Management.Automation/engine/InitialSessionState.cs +++ b/src/System.Management.Automation/engine/InitialSessionState.cs @@ -4204,7 +4204,7 @@ .FORWARDHELPCATEGORY Cmdlet $customPagerCommand = $tokens[0].Content if (!(Get-Command $customPagerCommand -ErrorAction Ignore)) { # Custom pager command is invalid, issue a warning. - Write-Warning ""Ignoring invalid custom-paging utility command line specified in `$env:PAGER: $env:PAGER"" + Write-Warning ""Custom-paging utility command not found. Ignoring command specified in `$env:PAGER: $env:PAGER"" } else { # This approach will preserve all the pagers args.