From 50e31e68bbb3a4dc00c064877514cce61cfb27d9 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Tue, 5 Jul 2016 14:44:44 -0700 Subject: [PATCH 01/11] Implement ReadLineSafe for Linux This uses System.Console instead of ConsoleControl to read keys into a SecureString in a portable manner. The Windows implementation is left untouched. --- .../host/msh/ConsoleHostUserInterface.cs | 57 +++++++++++++++++-- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs index 8dbf99f24..2c8b6c090 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs @@ -251,6 +251,10 @@ namespace Microsoft.PowerShell /// It also manages the cursor as keys are entered and "backspaced". However, it is possible that /// while this method is running, the console buffer contents could change. Then, its cursor mgmt /// will likely be messed up. + /// + /// Secondary implementation for Unix based on Console.ReadKey(), where + /// the advantage is portability through abstraction. Does not support + /// arrow key movement, but supports backspace. /// /// /// @@ -285,9 +289,6 @@ namespace Microsoft.PowerShell private object ReadLineSafe(bool isSecureString, char? printToken) { -#if LINUX - throw new PlatformNotSupportedException("Cannot read secure strings!"); -#else // Don't lock (instanceLock) in here -- the caller needs to do that... PreRead(); @@ -296,13 +297,21 @@ namespace Microsoft.PowerShell null; SecureString secureResult = new SecureString(); StringBuilder result = new StringBuilder(); +#if LINUX + bool treatControlCAsInput = Console.TreatControlCAsInput; +#endif ConsoleHandle handle = ConsoleControl.GetConioDeviceHandle(); ConsoleControl.ConsoleModes originalMode = ConsoleControl.GetMode(handle); bool isModeChanged = true; // assume ConsoleMode is changed so that if ReadLineSetMode // fails to return the value correctly, the original mode is // restored. +#endif + try { +#if LINUX + Console.TreatControlCAsInput = true; +#else // Ensure that we're in the proper line-input mode. ConsoleControl.ConsoleModes desiredMode = @@ -329,6 +338,7 @@ namespace Microsoft.PowerShell isModeChanged = false; } rawui.ClearKeyCache(); +#endif Coordinates originalCursorPos = rawui.CursorPosition; @@ -339,22 +349,40 @@ namespace Microsoft.PowerShell // end up having a immutable string holding the // secret in memory. // +#if LINUX + ConsoleKeyInfo keyInfo = Console.ReadKey(true); +#else uint unused = 0; string key = ConsoleControl.ReadConsole(handle, string.Empty, 1, false, out unused); +#endif +#if LINUX + // Handle Ctrl-C and Ctrl-D ending input + if ((keyInfo.Key == ConsoleKey.C || keyInfo.Key == ConsoleKey.D) + && keyInfo.Modifiers.HasFlag(ConsoleModifiers.Control)) +#else if (string.IsNullOrEmpty(key) || (char)3 == key[0]) +#endif { PipelineStoppedException e = new PipelineStoppedException(); throw e; } +#if LINUX + if (keyInfo.Key == ConsoleKey.Enter) +#else if ((char)13 == key[0]) +#endif { // // we are done if user presses ENTER key // break; } +#if LINUX + if (keyInfo.Key == ConsoleKey.Backspace) +#else if ((char)8 == key[0]) +#endif { // // for backspace, remove last char appended @@ -377,11 +405,19 @@ namespace Microsoft.PowerShell // if (isSecureString) { +#if LINUX + secureResult.AppendChar(keyInfo.KeyChar); +#else secureResult.AppendChar(key[0]); +#endif } else { +#if LINUX + result.Append(keyInfo.KeyChar); +#else result.Append(key); +#endif } if (!string.IsNullOrEmpty(printTokenString)) { @@ -391,12 +427,23 @@ namespace Microsoft.PowerShell } while (true); } +#if LINUX + catch (InvalidOperationException) + { + // ReadKey() failed so we stop + throw new PipelineStoppedException(); + } +#endif finally { +#if LINUX + Console.TreatControlCAsInput = treatControlCAsInput; +#else if (isModeChanged) { ConsoleControl.SetMode(handle, originalMode); } +#endif } WriteLineToConsole(); PostRead(result.ToString()); @@ -408,10 +455,8 @@ namespace Microsoft.PowerShell { return result; } -#endif } -#if !LINUX /// /// @@ -522,7 +567,7 @@ namespace Microsoft.PowerShell } - +#if !LINUX /// /// /// If is set on , unset it and return true; From 53a3b760cd7adcd7c2219300c8e1620e97c4f0f2 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Tue, 5 Jul 2016 15:24:44 -0700 Subject: [PATCH 02/11] Remove ReadLineSafe from known issues --- docs/KNOWNISSUES.md | 13 ------------- .../host/msh/ConsoleHostUserInterface.cs | 2 +- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/docs/KNOWNISSUES.md b/docs/KNOWNISSUES.md index 7356f041f..f06866c96 100644 --- a/docs/KNOWNISSUES.md +++ b/docs/KNOWNISSUES.md @@ -1,18 +1,5 @@ # Known Issues -## SecureString - -The `SecureString` class is *not* the well-known C# `SecureString`, as the -library is not available in .NET Core. However, PowerShell continues to use the -type for two main reasons: FullCLR compatibility, and as a specially-treated -type (that is, behavior is dependent on the type being `SecureString`). Instead, -the `SecureString` is Mono's completely not secure whatsoever, but open-source -and "compatible" stub that acts just like a plaintext `StringBuilder`. **It has -no encryption.** - -Additionally `ReadLineSafe` is not implemented, meaning `Get-Credential` fails -with `PlatformNotSupportedException`. - ## Files excluded from the build #### Microsoft.PowerShell.Commands.Management diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs index 2c8b6c090..8b4e7bb99 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs @@ -299,7 +299,7 @@ namespace Microsoft.PowerShell StringBuilder result = new StringBuilder(); #if LINUX bool treatControlCAsInput = Console.TreatControlCAsInput; -#endif +#else ConsoleHandle handle = ConsoleControl.GetConioDeviceHandle(); ConsoleControl.ConsoleModes originalMode = ConsoleControl.GetMode(handle); bool isModeChanged = true; // assume ConsoleMode is changed so that if ReadLineSetMode From 3572367c00b0cd28bb653d5fb830a260cb6d2875 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Tue, 5 Jul 2016 16:01:38 -0700 Subject: [PATCH 03/11] Implement ReadLineFromConsole for Linux --- .../host/msh/ConsoleHostUserInterface.cs | 109 ++++++++++++++++-- 1 file changed, 100 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs index 8b4e7bb99..b1a4ae3bf 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs @@ -1682,13 +1682,13 @@ namespace Microsoft.PowerShell private string ReadLineFromConsole(bool endOnTab, string initialContent, bool calledFromPipeline, ref string restOfLine, ref ReadLineResult result) { -#if LINUX - return ReadLineFromFile(initialContent); -#else +#if !LINUX ConsoleHandle handle = ConsoleControl.GetConioDeviceHandle(); +#endif PreRead(); // Ensure that we're in the proper line-input mode. +#if !LINUX ConsoleControl.ConsoleModes m = ConsoleControl.GetMode(handle); const ConsoleControl.ConsoleModes desiredMode = @@ -1702,6 +1702,7 @@ namespace Microsoft.PowerShell m |= desiredMode; ConsoleControl.SetMode(handle, m); } +#endif // If more characters are typed than you asked, then the next call to ReadConsole will return the // additional characters beyond those you requested. @@ -1718,18 +1719,33 @@ namespace Microsoft.PowerShell // If input is terminated with a break key (Ctrl-C, Ctrl-Break, Close, etc.), then the buffer will be // the empty string. - uint keyState = 0; - - rawui.ClearKeyCache(); - +#if LINUX + ConsoleKeyInfo keyInfo; string s = ""; + int index = 0; + int cursorLeft = Console.CursorLeft; + int cursorCurrent = cursorLeft; +#else + rawui.ClearKeyCache(); + uint keyState = 0; + string s = ""; +#endif do { +#if LINUX + keyInfo = Console.ReadKey(true); +#else s += ConsoleControl.ReadConsole(handle, initialContent, maxInputLineLength, endOnTab, out keyState); - Dbg.Assert(s != null, "s should never be null"); +#endif +#if LINUX + // Handle Ctrl-C and Ctrl-D ending input + if ((keyInfo.Key == ConsoleKey.C || keyInfo.Key == ConsoleKey.D) + && keyInfo.Modifiers.HasFlag(ConsoleModifiers.Control)) +#else if (s.Length == 0) +#endif { result = ReadLineResult.endedOnBreak; s = null; @@ -1743,13 +1759,29 @@ namespace Microsoft.PowerShell break; } +#if LINUX + if (keyInfo.Key == ConsoleKey.Enter) +#else if (s.EndsWith(Crlf, StringComparison.CurrentCulture)) +#endif { result = ReadLineResult.endedOnEnter; +#if LINUX + // We're intercepting characters, so we need to echo the newline + Console.Out.WriteLine(); +#else s = s.Remove(s.Length - Crlf.Length); +#endif break; } +#if LINUX + if (keyInfo.Key == ConsoleKey.Tab) + { + // This is unsupported + continue; + } +#else int i = s.IndexOf(Tab, StringComparison.CurrentCulture); if (endOnTab && i != -1) @@ -1799,6 +1831,66 @@ namespace Microsoft.PowerShell break; } +#endif +#if LINUX + if (keyInfo.Key == ConsoleKey.Backspace) + { + if (index > 0) + { + int length = s.Length; + s = s.Remove(index - 1, 1); + index--; + cursorCurrent = Console.CursorLeft; + Console.CursorLeft = cursorLeft; + Console.Out.Write(s.PadRight(length)); + Console.CursorLeft = cursorCurrent - 1; + } + continue; + } + + if (keyInfo.Key == ConsoleKey.Delete) + { + if (index < s.Length) + { + int length = s.Length; + s = s.Remove(index, 1); + cursorCurrent = Console.CursorLeft; + Console.CursorLeft = cursorLeft; + Console.Out.Write(s.PadRight(length)); + Console.CursorLeft = cursorCurrent; + } + continue; + } + + + if (keyInfo.Key == ConsoleKey.LeftArrow) + { + if (Console.CursorLeft > cursorLeft) + { + Console.CursorLeft--; + index--; + } + continue; + } + + if (keyInfo.Key == ConsoleKey.RightArrow) + { + if (Console.CursorLeft < cursorLeft + s.Length) + { + Console.CursorLeft++; + index++; + } + continue; + } + + // No special key was pressed, so echo and save the key + s = s.Insert(index, keyInfo.KeyChar.ToString()); + index++; + cursorCurrent = Console.CursorLeft; + Console.CursorLeft = cursorLeft; + Console.Out.Write(s); + Console.CursorLeft = cursorCurrent + 1; +#endif } while (true); @@ -1808,7 +1900,6 @@ namespace Microsoft.PowerShell "s should only be null if input ended with a break"); return s; -#endif } /// From 68875f0942ab0d5f802b9b55aacabb52526f9104 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Tue, 5 Jul 2016 19:30:29 -0700 Subject: [PATCH 04/11] Handle Ctrl-C while prompting --- .../host/msh/ConsoleHostUserInterface.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs index b1a4ae3bf..c2dedc666 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs @@ -1720,11 +1720,17 @@ namespace Microsoft.PowerShell // the empty string. #if LINUX + bool treatControlCAsInput = Console.TreatControlCAsInput; + + try + { + ConsoleKeyInfo keyInfo; string s = ""; int index = 0; int cursorLeft = Console.CursorLeft; int cursorCurrent = cursorLeft; + Console.TreatControlCAsInput = true; #else rawui.ClearKeyCache(); uint keyState = 0; @@ -1900,6 +1906,13 @@ namespace Microsoft.PowerShell "s should only be null if input ended with a break"); return s; +#if LINUX + } + finally + { + Console.TreatControlCAsInput = treatControlCAsInput; + } +#endif } /// From 8d23dad0ab62945219317a61adf2e245d9fcf840 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 7 Jul 2016 14:46:14 -0700 Subject: [PATCH 05/11] Add Read-Host regression test --- test/powershell/Host/Read-Host.Tests.ps1 | 20 ++++++++++++++ .../Host/assets/Read-Host.Output.expect | 27 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 test/powershell/Host/Read-Host.Tests.ps1 create mode 100755 test/powershell/Host/assets/Read-Host.Output.expect diff --git a/test/powershell/Host/Read-Host.Tests.ps1 b/test/powershell/Host/Read-Host.Tests.ps1 new file mode 100644 index 000000000..847198154 --- /dev/null +++ b/test/powershell/Host/Read-Host.Tests.ps1 @@ -0,0 +1,20 @@ +Describe "Read-Host" { + Context "[Console]::ReadKey() implementation on non-Windows" { + BeforeAll { + $powershell = Join-Path -Path $PsHome -ChildPath "powershell" + $assetsDir = Join-Path -Path $PSScriptRoot -ChildPath assets + if ($IsWindows) { + $ItArgs = @{ skip = $true } + } elseif (-not (Get-Command expect -ErrorAction Ignore)) { + $ItArgs = @{ pending = $true } + } else { + $ItArgs = @{ } + } + } + + It @ItArgs "Should output correctly" { + & (Join-Path $assetsDir "Read-Host.Output.expect") $powershell | Out-Null + $LASTEXITCODE | Should Be 0 + } + } +} diff --git a/test/powershell/Host/assets/Read-Host.Output.expect b/test/powershell/Host/assets/Read-Host.Output.expect new file mode 100755 index 000000000..7cfb718d6 --- /dev/null +++ b/test/powershell/Host/assets/Read-Host.Output.expect @@ -0,0 +1,27 @@ +#!/usr/bin/expect -f + +exp_internal 1 + +# Expects path to PowerShell as first argument +set powershell [lindex $argv 0] + +set timeout 5 + +spawn $powershell -nologo -noprofile Read-Host prompt + +expect "prompt: $" { + send "input\r" +} + +# This is the old, incorrect behavior +expect "input\r\ninput\r" { + exit 2 +} + +# This is the expected behavior +expect "input\r\n" { + exit 0 +} + +# Anything else is wrong +exit 1 From 5dd5aaf0074fba190350f6d7002990a0750cbb57 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Fri, 8 Jul 2016 11:47:41 -0700 Subject: [PATCH 06/11] ReadLineFromConsole improvements Also more guarding in ReadLineSafe --- .../host/msh/ConsoleHostUserInterface.cs | 92 ++++++++++++++++--- 1 file changed, 78 insertions(+), 14 deletions(-) diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs index c2dedc666..bb992699a 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs @@ -351,15 +351,15 @@ namespace Microsoft.PowerShell // #if LINUX ConsoleKeyInfo keyInfo = Console.ReadKey(true); + char k = keyInfo.KeyChar; #else uint unused = 0; string key = ConsoleControl.ReadConsole(handle, string.Empty, 1, false, out unused); #endif #if LINUX - // Handle Ctrl-C and Ctrl-D ending input - if ((keyInfo.Key == ConsoleKey.C || keyInfo.Key == ConsoleKey.D) - && keyInfo.Modifiers.HasFlag(ConsoleModifiers.Control)) + // Handle Ctrl-C ending input + if (keyInfo.Key == ConsoleKey.C && keyInfo.Modifiers.HasFlag(ConsoleModifiers.Control)) #else if (string.IsNullOrEmpty(key) || (char)3 == key[0]) #endif @@ -398,6 +398,14 @@ namespace Microsoft.PowerShell WriteBackSpace(originalCursorPos); } } +#if LINUX + else if (!(Char.IsLetterOrDigit(k) || Char.IsPunctuation(k) || Char.IsSymbol(k) || Char.IsWhiteSpace(k))) + { + // Whitelist valid input characters: letters, digits, punctuation, symbols whitespace + // This is to avoid edge cases like Pause, Print Screen, function keys, and Oem keys, etc. + continue; + } +#endif else { // @@ -406,7 +414,7 @@ namespace Microsoft.PowerShell if (isSecureString) { #if LINUX - secureResult.AppendChar(keyInfo.KeyChar); + secureResult.AppendChar(k); #else secureResult.AppendChar(key[0]); #endif @@ -414,7 +422,7 @@ namespace Microsoft.PowerShell else { #if LINUX - result.Append(keyInfo.KeyChar); + result.Append(k); #else result.Append(key); #endif @@ -1682,13 +1690,11 @@ namespace Microsoft.PowerShell private string ReadLineFromConsole(bool endOnTab, string initialContent, bool calledFromPipeline, ref string restOfLine, ref ReadLineResult result) { -#if !LINUX - ConsoleHandle handle = ConsoleControl.GetConioDeviceHandle(); -#endif PreRead(); // Ensure that we're in the proper line-input mode. #if !LINUX + ConsoleHandle handle = ConsoleControl.GetConioDeviceHandle(); ConsoleControl.ConsoleModes m = ConsoleControl.GetMode(handle); const ConsoleControl.ConsoleModes desiredMode = @@ -1720,6 +1726,11 @@ namespace Microsoft.PowerShell // the empty string. #if LINUX + // For Unix systems, we implement a basic readline loop around Console.ReadKey(), that + // supports backspace, arrow keys, Ctrl-C, and Ctrl-D. This readline is only used for + // interactive prompts (like Read-Host), otherwise it is assumed that PSReadLine is + // available. Therefore this explicitly does not support history or tab completion. + bool treatControlCAsInput = Console.TreatControlCAsInput; try @@ -1730,6 +1741,7 @@ namespace Microsoft.PowerShell int index = 0; int cursorLeft = Console.CursorLeft; int cursorCurrent = cursorLeft; + bool insertMode = true; Console.TreatControlCAsInput = true; #else rawui.ClearKeyCache(); @@ -1746,9 +1758,8 @@ namespace Microsoft.PowerShell #endif #if LINUX - // Handle Ctrl-C and Ctrl-D ending input - if ((keyInfo.Key == ConsoleKey.C || keyInfo.Key == ConsoleKey.D) - && keyInfo.Modifiers.HasFlag(ConsoleModifiers.Control)) + // Handle Ctrl-C ending input + if (keyInfo.Key == ConsoleKey.C && keyInfo.Modifiers.HasFlag(ConsoleModifiers.Control)) #else if (s.Length == 0) #endif @@ -1868,7 +1879,6 @@ namespace Microsoft.PowerShell continue; } - if (keyInfo.Key == ConsoleKey.LeftArrow) { if (Console.CursorLeft > cursorLeft) @@ -1889,9 +1899,63 @@ namespace Microsoft.PowerShell continue; } - // No special key was pressed, so echo and save the key - s = s.Insert(index, keyInfo.KeyChar.ToString()); + if (keyInfo.Key == ConsoleKey.UpArrow + || keyInfo.Key == ConsoleKey.DownArrow + || keyInfo.Key == ConsoleKey.PageUp + || keyInfo.Key == ConsoleKey.PageDown) + { + // Arrow/Page Up/down is unimplemented, so fail gracefully + continue; + } + + if (keyInfo.Key == ConsoleKey.Home + || (keyInfo.Key == ConsoleKey.A && keyInfo.Modifiers.HasFlag(ConsoleModifiers.Control))) + { + Console.CursorLeft = cursorLeft; + index = 0; + continue; + } + + if (keyInfo.Key == ConsoleKey.End + || (keyInfo.Key == ConsoleKey.E && keyInfo.Modifiers.HasFlag(ConsoleModifiers.Control))) + { + Console.CursorLeft = cursorLeft + s.Length; + index = s.Length; + continue; + } + + if (keyInfo.Key == ConsoleKey.Escape) + { + Console.CursorLeft = cursorLeft; + index = s.Length; + s = ""; + continue; + } + + if (keyInfo.Key == ConsoleKey.Insert) + { + // Toggle insert/overwrite mode + insertMode = !insertMode; + continue; + } + + char k = keyInfo.KeyChar; + if (!(Char.IsLetterOrDigit(k) || Char.IsPunctuation(k) || Char.IsSymbol(k) || Char.IsWhiteSpace(k))) + { + // Whitelist valid input characters: letters, digits, punctuation, symbols whitespace + // This is to avoid edge cases like Pause, Print Screen, function keys, and Oem keys, etc. + continue; + } + + // Modify string + if (!insertMode) // then overwrite mode + { + s = s.Remove(index, 1); + } + s = s.Insert(index, k.ToString()); index++; + + // Redisplay string cursorCurrent = Console.CursorLeft; Console.CursorLeft = cursorLeft; Console.Out.Write(s); From 332397e955eaea4e4166e1d7ef9f06f009801f60 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Fri, 8 Jul 2016 14:28:49 -0700 Subject: [PATCH 07/11] Bump expect timeout for slow CI systems --- test/powershell/Host/assets/Read-Host.Output.expect | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/powershell/Host/assets/Read-Host.Output.expect b/test/powershell/Host/assets/Read-Host.Output.expect index 7cfb718d6..a43044ee4 100755 --- a/test/powershell/Host/assets/Read-Host.Output.expect +++ b/test/powershell/Host/assets/Read-Host.Output.expect @@ -5,7 +5,7 @@ exp_internal 1 # Expects path to PowerShell as first argument set powershell [lindex $argv 0] -set timeout 5 +set timeout 60 spawn $powershell -nologo -noprofile Read-Host prompt From 85aa47b4895975d38d681ee34fea2ddaa8ec27bc Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Fri, 8 Jul 2016 16:16:35 -0700 Subject: [PATCH 08/11] Use Control-D for delete --- .../host/msh/ConsoleHostUserInterface.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs index bb992699a..83581f061 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs @@ -1865,7 +1865,8 @@ namespace Microsoft.PowerShell continue; } - if (keyInfo.Key == ConsoleKey.Delete) + if (keyInfo.Key == ConsoleKey.Delete + || (keyInfo.Key == ConsoleKey.D && keyInfo.Modifiers.HasFlag(ConsoleModifiers.Control))) { if (index < s.Length) { From 7ae448f0b1a27923722c4e05926fae79f5d69699 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Fri, 8 Jul 2016 16:17:32 -0700 Subject: [PATCH 09/11] Blacklist control characters rather than whitelist --- .../host/msh/ConsoleHostUserInterface.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs index 83581f061..146855863 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs @@ -399,10 +399,9 @@ namespace Microsoft.PowerShell } } #if LINUX - else if (!(Char.IsLetterOrDigit(k) || Char.IsPunctuation(k) || Char.IsSymbol(k) || Char.IsWhiteSpace(k))) + else if (Char.IsControl(k)) { - // Whitelist valid input characters: letters, digits, punctuation, symbols whitespace - // This is to avoid edge cases like Pause, Print Screen, function keys, and Oem keys, etc. + // blacklist control characters continue; } #endif @@ -1941,10 +1940,9 @@ namespace Microsoft.PowerShell } char k = keyInfo.KeyChar; - if (!(Char.IsLetterOrDigit(k) || Char.IsPunctuation(k) || Char.IsSymbol(k) || Char.IsWhiteSpace(k))) + else if (Char.IsControl(k)) { - // Whitelist valid input characters: letters, digits, punctuation, symbols whitespace - // This is to avoid edge cases like Pause, Print Screen, function keys, and Oem keys, etc. + // blacklist control characters continue; } From 152b1063752680631fdb3ba863ef1f5d1ca474ec Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Fri, 8 Jul 2016 16:19:05 -0700 Subject: [PATCH 10/11] Use Control-F/B for Right/Left arrow --- .../host/msh/ConsoleHostUserInterface.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs index 146855863..020ddc279 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs @@ -1879,7 +1879,8 @@ namespace Microsoft.PowerShell continue; } - if (keyInfo.Key == ConsoleKey.LeftArrow) + if (keyInfo.Key == ConsoleKey.LeftArrow + || (keyInfo.Key == ConsoleKey.B && keyInfo.Modifiers.HasFlag(ConsoleModifiers.Control))) { if (Console.CursorLeft > cursorLeft) { @@ -1889,7 +1890,8 @@ namespace Microsoft.PowerShell continue; } - if (keyInfo.Key == ConsoleKey.RightArrow) + if (keyInfo.Key == ConsoleKey.RightArrow + || (keyInfo.Key == ConsoleKey.F && keyInfo.Modifiers.HasFlag(ConsoleModifiers.Control))) { if (Console.CursorLeft < cursorLeft + s.Length) { From 64a5cae4cbda2b602066bcae0205c318899a8f12 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Fri, 8 Jul 2016 16:38:09 -0700 Subject: [PATCH 11/11] Remove char k --- .../host/msh/ConsoleHostUserInterface.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs index 020ddc279..9984e0a60 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs @@ -351,7 +351,6 @@ namespace Microsoft.PowerShell // #if LINUX ConsoleKeyInfo keyInfo = Console.ReadKey(true); - char k = keyInfo.KeyChar; #else uint unused = 0; string key = ConsoleControl.ReadConsole(handle, string.Empty, 1, false, out unused); @@ -399,7 +398,7 @@ namespace Microsoft.PowerShell } } #if LINUX - else if (Char.IsControl(k)) + else if (Char.IsControl(keyInfo.KeyChar)) { // blacklist control characters continue; @@ -413,7 +412,7 @@ namespace Microsoft.PowerShell if (isSecureString) { #if LINUX - secureResult.AppendChar(k); + secureResult.AppendChar(keyInfo.KeyChar); #else secureResult.AppendChar(key[0]); #endif @@ -421,7 +420,7 @@ namespace Microsoft.PowerShell else { #if LINUX - result.Append(k); + result.Append(keyInfo.KeyChar); #else result.Append(key); #endif @@ -1941,8 +1940,7 @@ namespace Microsoft.PowerShell continue; } - char k = keyInfo.KeyChar; - else if (Char.IsControl(k)) + if (Char.IsControl(keyInfo.KeyChar)) { // blacklist control characters continue; @@ -1953,7 +1951,7 @@ namespace Microsoft.PowerShell { s = s.Remove(index, 1); } - s = s.Insert(index, k.ToString()); + s = s.Insert(index, keyInfo.KeyChar.ToString()); index++; // Redisplay string