diff --git a/.travis.yml b/.travis.yml index c64f632053..b77ed236a6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,6 @@ matrix: - os: osx osx_image: xcode7.3 language: generic - env: PATH+=/usr/local/share/dotnet git: submodules: false before_install: diff --git a/PowerShellGitHubDev.psm1 b/PowerShellGitHubDev.psm1 index a8b00c5a0b..71ebbe33a0 100644 --- a/PowerShellGitHubDev.psm1 +++ b/PowerShellGitHubDev.psm1 @@ -221,10 +221,10 @@ function New-PSOptions { [switch]$FullCLR ) - $Top = if ($FullCLR) { - "$PSScriptRoot\src\Microsoft.PowerShell.ConsoleHost" + if ($FullCLR) { + $Top = "$PSScriptRoot/src/Microsoft.PowerShell.ConsoleHost" } else { - "$PSScriptRoot/src/Microsoft.PowerShell.CoreConsoleHost" + $Top = "$PSScriptRoot/src/powershell" } Write-Verbose "Top project directory is $Top" @@ -315,7 +315,7 @@ function Start-PSPester { [string]$Directory = "$PSScriptRoot/test/powershell" ) - & (Get-PSOutput) -c "Invoke-Pester $Flags $Directory/$Tests" + & (Get-PSOutput) -noprofile -c "Invoke-Pester $Flags $Directory/$Tests" if ($LASTEXITCODE -ne 0) { throw "$LASTEXITCODE Pester tests failed" } diff --git a/appveyor.yml b/appveyor.yml index bd4fdbcf8b..39d225f941 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -33,17 +33,17 @@ test_script: $ErrorActionPreference = 'Stop' # # CoreCLR - $env:CoreOutput = "$pwd\src\Microsoft.PowerShell.CoreConsoleHost\bin\Debug\netcoreapp1.0\win81-x64\publish" + $env:CoreOutput = Split-Path -Parent (Get-PSOutput -Options (New-PSOptions -Publish)) Write-Host -Foreground Green 'Run CoreCLR tests' $testResultsFile = "$pwd\TestsResults.xml" - & ("$env:CoreOutput\powershell.exe") -c "Invoke-Pester test/powershell -OutputFormat NUnitXml -OutputFile $testResultsFile" + & ("$env:CoreOutput\powershell.exe") -noprofile -noninteractive -c "Invoke-Pester test/powershell -OutputFormat NUnitXml -OutputFile $testResultsFile" (New-Object 'System.Net.WebClient').UploadFile("https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path $testResultsFile)) # # FullCLR - $env:FullOutput = "$pwd\src\Microsoft.PowerShell.ConsoleHost\bin\Debug\net451" + $env:FullOutput = Split-Path -Parent (Get-PSOutput -Options (New-PSOptions -FullCLR)) Write-Host -Foreground Green 'Run FullCLR tests' $testResultsFileFullCLR = "$pwd\TestsResults.FullCLR.xml" - Start-DevPSGitHub -binDir $env:FullOutput -NoNewWindow -ArgumentList '-command', "Import-Module .\src\Modules\Pester; Invoke-Pester test/fullCLR -OutputFormat NUnitXml -OutputFile $testResultsFileFullCLR" + Start-DevPSGitHub -binDir $env:FullOutput -NoNewWindow -ArgumentList '-noprofile', '-noninteractive', '-command', "Import-Module .\src\Modules\Pester; Invoke-Pester test/fullCLR -OutputFormat NUnitXml -OutputFile $testResultsFileFullCLR" (New-Object 'System.Net.WebClient').UploadFile("https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path $testResultsFileFullCLR)) # # Fail the build, if tests failed diff --git a/mapping.json b/mapping.json index 63b3f08d84..ebedb25cc7 100644 --- a/mapping.json +++ b/mapping.json @@ -1187,12 +1187,12 @@ "src/monad/monad/miscfiles/modules/Microsoft.PowerShell.Archive/ArchiveResources.psd1": "src/Modules/Microsoft.PowerShell.Archive/ArchiveResources.psd1", "src/monad/monad/miscfiles/modules/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psd1": "src/Modules/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psd1", "src/monad/monad/miscfiles/modules/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psm1": "src/Modules/Microsoft.PowerShell.Archive/Microsoft.PowerShell.Archive.psm1", - "src/monad/monad/miscfiles/modules/Microsoft.PowerShell.Diagnostics/CoreClr/Microsoft.PowerShell.Diagnostics.psd1": "src/Microsoft.PowerShell.CoreConsoleHost/Modules/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1", + "src/monad/monad/miscfiles/modules/Microsoft.PowerShell.Diagnostics/CoreClr/Microsoft.PowerShell.Diagnostics.psd1": "src/powershell/Modules/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1", "src/monad/monad/miscfiles/modules/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1": "src/Microsoft.PowerShell.ConsoleHost/Modules/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1", "src/monad/monad/miscfiles/modules/Microsoft.PowerShell.Host/Microsoft.PowerShell.Host.psd1": "src/Modules/Microsoft.PowerShell.Host/Microsoft.PowerShell.Host.psd1", "src/monad/monad/miscfiles/modules/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1": "src/Modules/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1", "src/monad/monad/miscfiles/modules/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1": "src/Modules/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1", - "src/monad/monad/miscfiles/modules/Microsoft.PowerShell.Utility/CoreClr/Microsoft.PowerShell.Utility.psd1": "src/Microsoft.PowerShell.CoreConsoleHost/Modules/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1", + "src/monad/monad/miscfiles/modules/Microsoft.PowerShell.Utility/CoreClr/Microsoft.PowerShell.Utility.psd1": "src/powershell/Modules/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1", "src/monad/monad/miscfiles/modules/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1": "src/Microsoft.PowerShell.ConsoleHost/Modules/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1", "src/monad/monad/miscfiles/modules/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psm1": "src/Modules/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psm1", "src/monad/monad/miscfiles/modules/Microsoft.WSMan.Management/Microsoft.WSMan.Management.psd1": "src/Modules/Microsoft.WSMan.Management/Microsoft.WSMan.Management.psd1", diff --git a/src/Microsoft.PowerShell.ConsoleHost/AssemblyInfo.cs b/src/Microsoft.PowerShell.ConsoleHost/AssemblyInfo.cs index dde5dcdf4c..fad3142bb2 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/AssemblyInfo.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/AssemblyInfo.cs @@ -2,12 +2,16 @@ using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; +#if !CORECLR using System.Runtime.ConstrainedExecution; using System.Security.Permissions; +#endif +#if CORECLR [assembly:AssemblyCulture("")] [assembly:NeutralResourcesLanguage("en-US")] - +[assembly:InternalsVisibleTo("powershell-tests")] +#else [assembly:AssemblyConfiguration("")] [assembly:AssemblyInformationalVersionAttribute (@"10.0.10011.16384")] [assembly:ReliabilityContractAttribute(Consistency.MayCorruptAppDomain, Cer.MayFail)] @@ -15,14 +19,16 @@ using System.Security.Permissions; [assembly:AssemblyDescription("Microsoft Windows PowerShell Console Host")] [assembly:System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.5")] +[assembly:System.Reflection.AssemblyFileVersion("10.0.10011.16384")] +[assembly:AssemblyKeyFileAttribute(@"..\signing\visualstudiopublic.snk")] +[assembly:System.Reflection.AssemblyDelaySign(true)] +#endif + [assembly:System.Runtime.InteropServices.ComVisible(false)] [assembly:System.Reflection.AssemblyVersion("3.0.0.0")] [assembly:System.Reflection.AssemblyProduct("Microsoft (R) Windows (R) Operating System")] [assembly:System.Reflection.AssemblyCopyright("Copyright (c) Microsoft Corporation. All rights reserved.")] [assembly:System.Reflection.AssemblyCompany("Microsoft Corporation")] -[assembly:System.Reflection.AssemblyFileVersion("10.0.10011.16384")] -[assembly:AssemblyKeyFileAttribute(@"..\signing\visualstudiopublic.snk")] -[assembly:System.Reflection.AssemblyDelaySign(true)] internal static class AssemblyStrings { diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/CommandLineParameterParser.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/CommandLineParameterParser.cs index 814614f93a..2f9a374e60 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/CommandLineParameterParser.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/CommandLineParameterParser.cs @@ -118,13 +118,13 @@ namespace Microsoft.PowerShell } } - internal bool ReadFromStdin + internal bool ExplicitReadCommandsFromStdin { get { Dbg.Assert(dirty, "Parse has not been called yet"); - return readFromStdin; + return explicitReadCommandsFromStdin; } } @@ -365,6 +365,12 @@ namespace Microsoft.PowerShell switchKey = switchKey.Substring(1); + // chop off the second dash so we're agnostic wrt specifying - or -- + if (SpecialCharacters.IsDash(switchKey[0])) + { + switchKey = switchKey.Substring(1); + } + if (MatchSwitch(switchKey, "help", "h") || MatchSwitch(switchKey, "?", "?")) { showHelp = true; @@ -482,7 +488,7 @@ namespace Microsoft.PowerShell { // the arg to -file is -, which is secret code for "read the commands from stdin with prompts" - readFromStdin = true; + explicitReadCommandsFromStdin = true; noPrompt = false; } else @@ -844,7 +850,7 @@ namespace Microsoft.PowerShell { // the arg to -command is -, which is secret code for "read the commands from stdin with no prompts" - readFromStdin = true; + explicitReadCommandsFromStdin = true; noPrompt = true; ++i; @@ -859,7 +865,7 @@ namespace Microsoft.PowerShell return false; } - if (!parent.IsStandardInputRedirected) + if (!Console.IsInputRedirected) { ui.WriteErrorLine(CommandLineParameterParserStrings.StdinNotRedirected); showHelp = true; @@ -968,7 +974,7 @@ namespace Microsoft.PowerShell // default is sta. private bool? staMode = null; private bool noExit = true; - private bool readFromStdin; + private bool explicitReadCommandsFromStdin; private bool noPrompt; private string commandLineCommand; private bool wasCommandEncoded; diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleControl.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleControl.cs index 830a2f03c3..cc385c1ee7 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleControl.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleControl.cs @@ -1,3 +1,4 @@ +#if !PORTABLE /********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ @@ -3685,4 +3686,4 @@ namespace Microsoft.PowerShell private static PSTraceSource tracer = PSTraceSource.GetTracer("ConsoleControl", "Console control methods"); } } // namespace - +#endif diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs index 5eeef3c10f..3b7f69e662 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs @@ -170,8 +170,15 @@ namespace Microsoft.PowerShell { try { - var profileDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + - @"\Microsoft\Windows\PowerShell"; + string profileDir; + if (Platform.IsWindows) + { + profileDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + + @"\Microsoft\Windows\PowerShell"; + } else + { + profileDir = System.IO.Path.Combine(Environment.GetEnvironmentVariable("HOME"), ".powershell"); + } if (!Directory.Exists(profileDir)) { @@ -190,9 +197,11 @@ namespace Microsoft.PowerShell System.Threading.Thread.CurrentThread.Name = "ConsoleHost main thread"; theConsoleHost = ConsoleHost.CreateSingletonInstance(configuration); +#if !PORTABLE theConsoleHost.BindBreakHandler(); +#endif - PSHost.IsStdOutputRedirected = theConsoleHost.IsStandardOutputRedirected; + PSHost.IsStdOutputRedirected = Console.IsOutputRedirected; if (args == null) { @@ -268,6 +277,7 @@ namespace Microsoft.PowerShell +#if !PORTABLE /// /// /// The break handler for the program. Dispatches a break event to the current Executor. @@ -307,6 +317,7 @@ namespace Microsoft.PowerShell return false; } } +#endif private static bool BreakIntoDebugger() { @@ -425,8 +436,10 @@ namespace Microsoft.PowerShell // call the console APIs directly, instead of ui.rawui.FlushInputHandle, as ui may be finalized // already if this thread is lagging behind the main thread. +#if !PORTABLE ConsoleHandle handle = ConsoleControl.GetInputHandle(); ConsoleControl.FlushConsoleInputBuffer(handle); +#endif ConsoleHost.SingletonInstance.breakHandlerThread = null; } @@ -1019,11 +1032,13 @@ namespace Microsoft.PowerShell #endif } +#if !PORTABLE private void BindBreakHandler() { breakHandlerGcHandle = GCHandle.Alloc(new ConsoleControl.BreakHandler(MyBreakHandler)); ConsoleControl.AddBreakHandler((ConsoleControl.BreakHandler)breakHandlerGcHandle.Target); } +#endif #if !CORECLR // Not used on NanoServer: CurrentDomain.UnhandledException not supported on CoreCLR private void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args) @@ -1076,9 +1091,11 @@ namespace Microsoft.PowerShell { if (!isDisposed) { +#if !PORTABLE Dbg.Assert(breakHandlerGcHandle != null, "break handler should be set"); ConsoleControl.RemoveBreakHandler(); breakHandlerGcHandle.Free(); +#endif if (isDisposingNotFinalizing) { @@ -1193,7 +1210,7 @@ namespace Microsoft.PowerShell //If this shell is invoked in minishell interop mode and error is redirected, //always write data in error stream in xml format. - if (IsInteractive == false && IsStandardErrorRedirected && wasInitialCommandEncoded) + if (IsInteractive == false && Console.IsErrorRedirected && wasInitialCommandEncoded) { format = Serialization.DataFormat.XML; } @@ -1205,19 +1222,7 @@ namespace Microsoft.PowerShell { get { - if (IsInteractive) - { - return false; - } - - if ( - (OutputFormat != Serialization.DataFormat.Text) - || (IsStandardInputRedirected && !ui.ReadFromStdin)) - { - return true; - } - - return false; + return !IsInteractive && ((OutputFormat != Serialization.DataFormat.Text) || Console.IsInputRedirected); } } @@ -1236,7 +1241,7 @@ namespace Microsoft.PowerShell new WrappedSerializer( outputFormat, "Output", - IsStandardOutputRedirected ? StandardOutputWriter : ConsoleTextWriter); + Console.IsOutputRedirected ? Console.Out : ConsoleTextWriter); } return outputSerializer; } @@ -1252,7 +1257,7 @@ namespace Microsoft.PowerShell new WrappedSerializer( ErrorFormat, "Error", - IsStandardErrorRedirected ? StandardErrorWriter : ConsoleTextWriter); + Console.IsErrorRedirected ? Console.Error : ConsoleTextWriter); } return errorSerializer; } @@ -1328,7 +1333,7 @@ namespace Microsoft.PowerShell inputFormat = cpp.InputFormat; wasInitialCommandEncoded = cpp.WasInitialCommandEncoded; - ui.ReadFromStdin = cpp.ReadFromStdin; + ui.ReadFromStdin = cpp.ExplicitReadCommandsFromStdin || Console.IsInputRedirected; ui.NoPrompt = cpp.NoPrompt; ui.ThrowOnReadAndPrompt = cpp.ThrowOnReadAndPrompt; noExit = cpp.NoExit; @@ -1486,8 +1491,15 @@ namespace Microsoft.PowerShell private bool LoadPSReadline() { - return ((cpp.InitialCommand == null && cpp.File == null) || cpp.NoExit) && - (!IsStandardInputRedirected && !cpp.NonInteractive && !cpp.ReadFromStdin); + // Don't load PSReadline if: + // * we don't think the process will be interactive, e.g. -command or -file + // - exception: when -noexit is specified, we will be interactive after the command/file finishes + // * -noniteractive: this should be obvious, they've asked that we don't every prompt + // + // Note that PSReadline doesn't support redirected stdin/stdout, but we don't check that here because + // a future version might, and we should automatically load it at that unknown point in the future. + // PSReadline will ideally fall back to Console.ReadLine or whatever when stdin/stdout is redirected. + return ((cpp.InitialCommand == null && cpp.File == null) || cpp.NoExit) && !cpp.NonInteractive; } /// @@ -1528,7 +1540,7 @@ namespace Microsoft.PowerShell { // Create and open Runspace with PSReadline. defaultImportModulesList = DefaultInitialSessionState.Modules; - DefaultInitialSessionState.ImportPSModule(new[] { "PSReadline" }); + DefaultInitialSessionState.ImportPSModule(new[] { "PSReadLine" }); consoleRunspace = RunspaceFactory.CreateRunspace(this, DefaultInitialSessionState); try { @@ -1787,6 +1799,9 @@ namespace Microsoft.PowerShell if (AstSearcher.IsUsingDollarInput(parsedInput)) { executionOptions |= Executor.ExecutionOptions.ReadInputObjects; + + // We will consume all of the input to pass to the script, so don't try to read commands from stdin. + ui.ReadFromStdin = false; } exec.ExecuteCommandAsyncHelper(tempPipeline, out e1, executionOptions); @@ -1846,6 +1861,9 @@ namespace Microsoft.PowerShell if (AstSearcher.IsUsingDollarInput(parsedInput)) { executionOptions |= Executor.ExecutionOptions.ReadInputObjects; + + // We will consume all of the input to pass to the script, so don't try to read commands from stdin. + ui.ReadFromStdin = false; } exec.ExecuteCommandAsyncHelper(tempPipeline, out e1, executionOptions); @@ -2049,205 +2067,6 @@ namespace Microsoft.PowerShell #endregion non-overrides -#region stdio redirection - - - - private delegate void InitializeStandardHandleDelegate(NakedWin32Handle handle); - - - [ArchitectureSensitive] - private bool IsStandardHandleRedirected( - ConsoleControl.StandardHandleId handleId, - ref bool isHandleRedirectionDetermined, - ref bool isHandleRedirected, - InitializeStandardHandleDelegate handleInit) - { - // lock because we update a bunch of instance vars here... - - lock (hostGlobalLock) - { - if (!isHandleRedirectionDetermined) - { - isHandleRedirected = false; - - NakedWin32Handle stdHandle = ConsoleControl.GetStdHandle(handleId); - SafeFileHandle sfh = new SafeFileHandle(stdHandle, false); - if (!sfh.IsInvalid) - { - var fileType = ConsoleControl.NativeMethods.GetFileType(stdHandle); - if (fileType != ConsoleControl.NativeMethods.FileType.Char) - { - // stdHandle is not a console handle; so it has been redirected. - - isHandleRedirected = true; - handleInit(stdHandle); - } - } - - isHandleRedirectionDetermined = true; - } - } - - return isHandleRedirected; - } - - internal bool IsStandardOutputRedirected - { - get - { - if (initStandardOutDelegate == null) - { - initStandardOutDelegate = new InitializeStandardHandleDelegate(InitializeStandardOutputWriter); - } - - return - IsStandardHandleRedirected( - ConsoleControl.StandardHandleId.Output, - ref isStandardOutputRedirectionDetermined, - ref isStandardOutputRedirected, - initStandardOutDelegate); - } - } - - internal bool IsStandardInputRedirected - { - get - { - if (initStandardInDelegate == null) - { - initStandardInDelegate = new InitializeStandardHandleDelegate(InitializeStandardInputReader); - } - - return - IsStandardHandleRedirected( - ConsoleControl.StandardHandleId.Input, - ref isStandardInputRedirectionDetermined, - ref isStandardInputRedirected, - initStandardInDelegate); - } - } - - internal bool IsStandardErrorRedirected - { - get - { - if (initStandardErrorDelegate == null) - { - initStandardErrorDelegate = new InitializeStandardHandleDelegate(InitializeStandardErrorWriter); - } - - return - IsStandardHandleRedirected( - ConsoleControl.StandardHandleId.Error, - ref isStandardErrorRedirectionDetermined, - ref isStandardErrorRedirected, - initStandardErrorDelegate); - } - } - - [ArchitectureSensitive] - private void InitializeStandardInputReader(NakedWin32Handle stdHandle) - { - Dbg.Assert(standardInputReader == null, "standardInputReader should not exist at this point"); - - // stdin has been redirected. Use ReadFile instead of ReadConsole. - - uint codePage = (uint) ConsoleControl.NativeMethods.GetConsoleCP(); - Encoding encoding = Encoding.GetEncoding((int) codePage); - - try - { - Stream s = - new FileStream( - - // since stdHandle was retreived not with an open, flag its safe wrapper as not needing to be closed. - - new SafeFileHandle(stdHandle, false), - FileAccess.Read); - - // The "false" flag here means "don't detect the byte order mark in the input stream." I hope that's the right - // thing. - -#if CORECLR // There is no TextReader.Synchronized on CoreCLR - standardInputReader = new StreamReader(s, encoding, false); -#else - standardInputReader = TextReader.Synchronized(new StreamReader(s, encoding, false)); -#endif - } - catch (System.IO.IOException) - { -#if CORECLR // There is no TextReader.Synchronized on CoreCLR - standardInputReader = new StringReader(""); -#else - standardInputReader = TextReader.Synchronized(new StringReader("")); -#endif - } - } - - [ArchitectureSensitive] - private void InitializeStandardOutputWriter(NakedWin32Handle stdHandle) - { - Dbg.Assert(standardOutputWriter == null, "standardOutputWriter should not exist at this point"); - standardOutputWriter = Console.Out; - } - - - - [ArchitectureSensitive] - private void InitializeStandardErrorWriter(NakedWin32Handle stdHandle) - { - Dbg.Assert(standardErrorWriter == null, "standardErrorWriter should not exist at this point"); - standardErrorWriter = Console.Error; - } - - internal TextWriter StandardOutputWriter - { - get - { - if (IsStandardOutputRedirected) - { - Dbg.Assert(standardOutputWriter != null, "standardOutputWriter should be initialized"); - - return standardOutputWriter; - } - - return null; - } - } - - internal TextWriter StandardErrorWriter - { - get - { - if (IsStandardErrorRedirected) - { - Dbg.Assert(standardErrorWriter != null, "standardErrorWriter should be initialized"); - - return standardErrorWriter; - } - - return null; - } - } - - internal TextReader StandardInReader - { - get - { - if (IsStandardInputRedirected) - { - Dbg.Assert(standardInputReader != null, "standardInputReader should be initialized"); - - return standardInputReader; - } - - return null; - } - } - -#endregion stdio redirection - #region debugger /// @@ -2544,12 +2363,6 @@ namespace Microsoft.PowerShell bool inBlockMode = false; bool previousResponseWasEmpty = false; StringBuilder inputBlock = new StringBuilder(); - bool displayPrompt = false; - // If the prompt has not been displayed in the native code or if the input loop is nested, we need to display the prompt here - if (!parent.promptDisplayedInNativeCode || inputLoopIsNested) - { - displayPrompt = true; - } while (!parent.ShouldEndSession && !shouldExit) { @@ -2559,53 +2372,42 @@ namespace Microsoft.PowerShell string prompt = null; string line = null; - if (displayPrompt) + if (!ui.NoPrompt) { - if (!ui.NoPrompt) + if (inBlockMode) { - if (inBlockMode) - { - // use a special prompt that denotes block mode + // use a special prompt that denotes block mode - prompt = ">> "; - } - else - { - // Make sure the cursor is at the start of the line - some external programs don't - // write a newline, so we do that for them. - if (ui.RawUI.CursorPosition.X != 0) - ui.WriteLine(); - - // Evaluate any suggestions - if(! previousResponseWasEmpty) - { - EvaluateSuggestions(ui); - } - - // Then output the prompt - if (this.parent.InDebugMode) - { - prompt = EvaluateDebugPrompt(); - } - if (prompt == null) - { - prompt = EvaluatePrompt(); - } - } - ui.Write(prompt); + prompt = ">> "; } - previousResponseWasEmpty = false; - // There could be a profile. So there could be a user defined custom readline command - line = ui.ReadLineWithTabCompletion(exec, useUserDefinedCustomReadLine: true); + else + { + // Make sure the cursor is at the start of the line - some external programs don't + // write a newline, so we do that for them. + if (ui.RawUI.CursorPosition.X != 0) + ui.WriteLine(); + // Evaluate any suggestions + if(! previousResponseWasEmpty) + { + EvaluateSuggestions(ui); + } + + // Then output the prompt + if (this.parent.InDebugMode) + { + prompt = EvaluateDebugPrompt(); + } + if (prompt == null) + { + prompt = EvaluatePrompt(); + } + } + ui.Write(prompt); } - else - { - previousResponseWasEmpty = false; - // There are no profiles. So there is no user defined custom readline command - line = ui.ReadLineWithTabCompletion(exec, useUserDefinedCustomReadLine: false); - displayPrompt = true; - } + previousResponseWasEmpty = false; + // There could be a profile. So there could be a user defined custom readline command + line = ui.ReadLineWithTabCompletion(exec); // line will be null in the case that Ctrl-C terminated the input @@ -2623,7 +2425,7 @@ namespace Microsoft.PowerShell } inBlockMode = false; - if (parent.IsStandardInputRedirected) + if (Console.IsInputRedirected) { // null is also the result of reading stdin to EOF. @@ -2711,7 +2513,7 @@ namespace Microsoft.PowerShell } else { - if (parent.IsRunningAsync) + if (parent.IsRunningAsync && !parent.IsNested) { exec.ExecuteCommandAsync(line, out e, Executor.ExecutionOptions.AddOutputter | Executor.ExecutionOptions.AddToHistory); } @@ -3047,7 +2849,9 @@ namespace Microsoft.PowerShell /// private RunspaceRef runspaceRef; +#if !PORTABLE private GCHandle breakHandlerGcHandle; +#endif private System.Threading.Thread breakHandlerThread; private bool isDisposed; internal ConsoleHostUserInterface ui; @@ -3076,18 +2880,6 @@ namespace Microsoft.PowerShell private bool shouldEndSession; private int beginApplicationNotifyCount; - private bool isStandardOutputRedirectionDetermined; - private bool isStandardOutputRedirected; - private TextWriter standardOutputWriter; - private bool isStandardErrorRedirectionDetermined; - private bool isStandardErrorRedirected; - private TextWriter standardErrorWriter; - private bool isStandardInputRedirectionDetermined; - private bool isStandardInputRedirected; - private TextReader standardInputReader; - private InitializeStandardHandleDelegate initStandardOutDelegate; - private InitializeStandardHandleDelegate initStandardInDelegate; - private InitializeStandardHandleDelegate initStandardErrorDelegate; private ConsoleTextWriter consoleWriter; private WrappedSerializer outputSerializer; private WrappedSerializer errorSerializer; diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostRawUserInterface.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostRawUserInterface.cs index 12bf65b80d..79b390d045 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostRawUserInterface.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostRawUserInterface.cs @@ -1,3 +1,4 @@ +#if !PORTABLE /********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ @@ -1499,3 +1500,280 @@ namespace Microsoft.PowerShell } } // namespace +#else + +// Managed code only implementation for portability + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Management.Automation; +using System.Management.Automation.Runspaces; +using System.Management.Automation.Host; +using System.Globalization; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace Microsoft.PowerShell +{ + // this is all originally from https://msdn.microsoft.com/en-us/library/ee706570%28v=vs.85%29.aspx + + internal sealed class ConsoleHostRawUserInterface : PSHostRawUserInterface + { + private ConsoleColor defaultForeground = ConsoleColor.Gray; + + private ConsoleColor defaultBackground = ConsoleColor.Black; + + private ConsoleHostUserInterface parent = null; + + internal ConsoleHostRawUserInterface(ConsoleHostUserInterface mshConsole) : base() + { + defaultForeground = ForegroundColor; + defaultBackground = BackgroundColor; + parent = mshConsole; + } + + /// + /// Gets or sets the background color of the displayed text. + /// This maps to the corresponding Console.Background property. + /// + public override ConsoleColor BackgroundColor + { + get { return Console.BackgroundColor; } + set { Console.BackgroundColor = value; } + } + + // TODO: Make wrap width user-customizable. + private static Size WrapSize = new Size(80, 40); + + /// + /// Gets or sets the size of the host buffer. In this example the + /// buffer size is adapted from the Console buffer size members. + /// + public override Size BufferSize + { + get + { + // When stdout is redirected, the buffer size is (0, 0); + // however, this is still queried for use in formatting, so we + // provide the WrapSize. + if (Console.IsOutputRedirected) + { + return WrapSize; + } + else + { + return new Size(Console.BufferWidth, Console.BufferHeight); + } + } + set { Console.SetBufferSize(value.Width, value.Height); } + } + + /// + /// Gets or sets the cursor position. In this example this + /// functionality is not needed so the property throws a + /// NotImplementException exception. + /// + public override Coordinates CursorPosition + { + get { return new Coordinates(Console.CursorLeft, Console.CursorTop); } + set { Console.SetCursorPosition(value.X, value.Y); } + } + + /// + /// Gets or sets the size of the displayed cursor. In this example + /// the cursor size is taken directly from the Console.CursorSize + /// property. + /// + public override int CursorSize + { + get { return Console.CursorSize; } + set { Console.CursorSize = value; } + } + + /// + /// Gets or sets the foreground color of the displayed text. + /// This maps to the corresponding Console.ForgroundColor property. + /// + public override ConsoleColor ForegroundColor + { + get { return Console.ForegroundColor; } + set { Console.ForegroundColor = value; } + } + + /// + /// Gets a value indicating whether the user has pressed a key. This maps + /// to the corresponding Console.KeyAvailable property. + /// + public override bool KeyAvailable + { + get { return Console.KeyAvailable; } + } + + /// + /// Gets the dimensions of the largest window that could be + /// rendered in the current display, if the buffer was at the least + /// that large. This example uses the Console.LargestWindowWidth and + /// Console.LargestWindowHeight properties to determine the returned + /// value of this property. + /// + public override Size MaxPhysicalWindowSize + { + get { return new Size(Console.LargestWindowWidth, Console.LargestWindowHeight); } + } + + /// + /// Gets the dimentions of the largest window size that can be + /// displayed. This example uses the Console.LargestWindowWidth and + /// console.LargestWindowHeight properties to determine the returned + /// value of this property. + /// + public override Size MaxWindowSize + { + get { return new Size(Console.LargestWindowWidth, Console.LargestWindowHeight); } + } + + /// + /// Gets or sets the position of the displayed window. This example + /// uses the Console window position APIs to determine the returned + /// value of this property. + /// + public override Coordinates WindowPosition + { + get { return new Coordinates(Console.WindowLeft, Console.WindowTop); } + set { Console.SetWindowPosition(value.X, value.Y); } + } + + /// + /// Gets or sets the size of the displayed window. This example + /// uses the corresponding Console window size APIs to determine the + /// returned value of this property. + /// + public override Size WindowSize + { + get + { + if (Console.IsOutputRedirected) + { + return WrapSize; + } + else + { + return new Size(Console.WindowWidth, Console.WindowHeight); + } + } + set { Console.SetWindowSize(value.Width, value.Height); } + } + + /// + /// Cached Window Title, for systems that needs it + /// + private string title = String.Empty; + + /// + /// Gets or sets the title of the displayed window. The example + /// maps the Console.Title property to the value of this property. + /// + public override string WindowTitle + { + get + { + // In Unix/Linux systems, Console.Title current results in a not-implemented + // exception. In that case, we return a cached copy of title that was set earlier. + // Obviously, this will not work if: 1) Title was never set in PowerShell, or 2) + // one sets the windows's title outside of PowerShell. + string result; + try + { + result = Console.Title; + } + catch (PlatformNotSupportedException) + { + return title; + } + return result; + } + + set + { + Console.Title = value; + title = value; + } + } + + /// + /// This API resets the input buffer. In this example this + /// functionality is not needed so the method returns nothing. + /// + public override void FlushInputBuffer() + { + } + + /// + /// This API returns a rectangular region of the screen buffer. In + /// this example this functionality is not needed so the method throws + /// a NotImplementException exception. + /// + /// Defines the size of the rectangle. + /// Throws a NotImplementedException exception. + public override BufferCell[,] GetBufferContents(Rectangle rectangle) + { + throw new NotImplementedException("The method or operation is not implemented."); + } + + /// + /// This API reads a pressed, released, or pressed and released keystroke + /// from the keyboard device, blocking processing until a keystroke is + /// typed that matches the specified keystroke options. + /// + /// Unused + public override KeyInfo ReadKey(ReadKeyOptions options) + { + ConsoleKeyInfo key = Console.ReadKey(); + return new KeyInfo((int)key.Key, key.KeyChar, new ControlKeyStates(), true); + } + + /// + /// This API crops a region of the screen buffer. In this example + /// this functionality is not needed so the method throws a + /// NotImplementException exception. + /// + /// The region of the screen to be scrolled. + /// The region of the screen to receive the + /// source region contents. + /// The region of the screen to include in the operation. + /// The character and attributes to be used to fill all cell. + public override void ScrollBufferContents(Rectangle source, Coordinates destination, Rectangle clip, BufferCell fill) + { + throw new NotImplementedException("The method or operation is not implemented."); + } + + /// + /// This method copies an array of buffer cells into the screen buffer + /// at a specified location. In this example this functionality is + /// not needed so the method throws a NotImplementedException exception. + /// + /// The parameter is not used. + /// The parameter is not used. + public override void SetBufferContents(Coordinates origin, + BufferCell[,] contents) + { + throw new NotImplementedException("The method or operation is not implemented."); + } + + /// + /// This method copies a given character, foreground color, and background + /// color to a region of the screen buffer. In this example this + /// functionality is not needed so the method throws a + /// NotImplementException exception./// + /// Defines the area to be filled. + /// Defines the fill character. + public override void SetBufferContents(Rectangle rectangle, BufferCell fill) + { + throw new NotImplementedException("The method or operation is not implemented."); + } + } +} +#endif diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs index 569bceb899..9e11aebf2a 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs @@ -14,7 +14,9 @@ using System.Management.Automation.Internal; using System.Management.Automation.Host; using System.Security; using Dbg = System.Management.Automation.Diagnostics; +#if !PORTABLE using ConsoleHandle = Microsoft.Win32.SafeHandles.SafeFileHandle; +#endif namespace Microsoft.PowerShell { @@ -58,7 +60,11 @@ namespace Microsoft.PowerShell this.parent = parent; this.rawui = new ConsoleHostRawUserInterface(this); + isInteractiveTestToolListening = false; +#if PORTABLE + this._supportsVirtualTerminal = true; +#else // Turn on virtual terminal if possible. var handle = ConsoleControl.GetActiveScreenBufferHandle(); var m = ConsoleControl.GetMode(handle); @@ -71,9 +77,7 @@ namespace Microsoft.PowerShell // systems ignore the setting. m = ConsoleControl.GetMode(handle); this._supportsVirtualTerminal = (m & ConsoleControl.ConsoleModes.VirtualTerminal) != 0; - - isInteractiveTestToolListening = false; - isTestingShiftTab = false; +#endif } /// @@ -140,17 +144,7 @@ namespace Microsoft.PowerShell /// /// - internal bool ReadFromStdin - { - get - { - return readFromStdin; - } - set - { - readFromStdin = value; - } - } + internal bool ReadFromStdin { get; set; } /// /// @@ -284,6 +278,9 @@ namespace Microsoft.PowerShell private object ReadLineSafe(bool isSecureString, char? printToken) { +#if PORTABLE + throw new PlatformNotSupportedException("Cannot read secure strings!"); +#else // Don't lock (instanceLock) in here -- the caller needs to do that... PreRead(); @@ -404,8 +401,11 @@ namespace Microsoft.PowerShell { return result; } +#endif } +#if !PORTABLE + /// /// /// Handle writing print token with proper cursor adjustment for ReadLineSafe @@ -535,7 +535,6 @@ namespace Microsoft.PowerShell /// false otherwise /// /// - private static bool shouldUnsetMode( ConsoleControl.ConsoleModes flagToUnset, ref ConsoleControl.ConsoleModes m) @@ -547,11 +546,14 @@ namespace Microsoft.PowerShell } return false; } +#endif #region WriteToConsole internal void WriteToConsole(string value, bool transcribeResult) { + +#if !PORTABLE ConsoleHandle handle = ConsoleControl.GetActiveScreenBufferHandle(); // Ensure that we're in the proper line-output mode. We don't lock here as it does not matter if we @@ -568,17 +570,21 @@ namespace Microsoft.PowerShell m |= desiredMode; ConsoleControl.SetMode(handle, m); } +#endif PreWrite(); // This is atomic, so we don't lock here... +#if !PORTABLE ConsoleControl.WriteConsole(handle, value); +#else + Console.Out.Write(value); +#endif - if (isInteractiveTestToolListening && parent.IsStandardOutputRedirected) + if (isInteractiveTestToolListening && Console.IsOutputRedirected) { - Dbg.Assert(parent.StandardOutputWriter != null, "stdout writer should be initialized"); - parent.StandardOutputWriter.Write(value); + Console.Out.Write(value); } if (transcribeResult) @@ -659,10 +665,7 @@ namespace Microsoft.PowerShell // If the test hook is set, write to it and continue. if (_h != null) _h.Write(value); - TextWriter writer = - (!parent.IsStandardOutputRedirected || parent.IsInteractive) - ? parent.ConsoleTextWriter - : parent.StandardOutputWriter; + TextWriter writer = Console.IsOutputRedirected ? Console.Out : parent.ConsoleTextWriter; if (parent.IsRunningAsync) { @@ -1362,9 +1365,9 @@ namespace Microsoft.PowerShell } TextWriter writer = - (!parent.IsStandardErrorRedirected || parent.IsInteractive) + (!Console.IsErrorRedirected || parent.IsInteractive) ? parent.ConsoleTextWriter - : parent.StandardErrorWriter; + : Console.Error; if (parent.ErrorFormat == Serialization.DataFormat.XML) { @@ -1378,7 +1381,7 @@ namespace Microsoft.PowerShell WriteLine(errorForegroundColor, errorBackgroundColor, value); else - parent.StandardErrorWriter.Write(value + Crlf); + Console.Error.Write(value + Crlf); } } @@ -1480,9 +1483,9 @@ namespace Microsoft.PowerShell - // We don't use System.Environment.NewLine because we are very platform specific with our use of the win32 console APIs + // We use System.Environment.NewLine because we are platform-agnostic - internal const string Crlf = "\x000D\x000A"; + internal static string Crlf = System.Environment.NewLine; private const string Tab = "\x0009"; internal enum ReadLineResult @@ -1555,21 +1558,75 @@ namespace Microsoft.PowerShell // If the test hook is set, read from it. if (_h != null) return _h.ReadLine(); - string s = ""; - if (parent.IsStandardInputRedirected && readFromStdin) + string restOfLine = null; + + string s = ReadFromStdin + ? ReadLineFromFile(initialContent) + : ReadLineFromConsole(endOnTab, initialContent, calledFromPipeline, ref restOfLine, ref result); + + if (transcribeResult) { - // When reading from a file handle instead of a console, endOnTab and initial content are ignored. - - // StreamReader.ReadLine simply returns null when EOF is reached. - - s = parent.StandardInReader.ReadLine(); - if (endOnTab && !string.IsNullOrEmpty(s) && s.IndexOf(Tab, StringComparison.OrdinalIgnoreCase) != -1) - { - result = isTestingShiftTab ? ReadLineResult.endedOnShiftTab : ReadLineResult.endedOnTab; - return s; - } - return s; + PostRead(s); } + else + { + PostRead(); + } + + if (restOfLine != null) + s += restOfLine; + + return s; + } + + private string ReadLineFromFile(string initialContent) + { + var sb = new StringBuilder(); + if (initialContent != null) + { + sb.Append(initialContent); + } + + while (true) + { + var inC = Console.In.Read(); + if (inC == -1) + { + // EOF - we return null which tells our caller to exit + return null; + } + + var c = unchecked((char)inC); + if (!NoPrompt) Console.Out.Write(c); + + if (c == '\r') + { + // Treat as newline, but consume \n if there is one. + if (Console.In.Peek() == '\n') + { + if (!NoPrompt) Console.Out.Write('\n'); + Console.In.Read(); + } + sb.Append('\n'); + break; + } + + sb.Append(c); + + if (c == '\n') + { + break; + } + } + + return sb.ToString(); + } + + private string ReadLineFromConsole(bool endOnTab, string initialContent, bool calledFromPipeline, ref string restOfLine, ref ReadLineResult result) + { +#if PORTABLE + return ReadLineFromFile(initialContent); +#else ConsoleHandle handle = ConsoleControl.GetInputHandle(); PreRead(); @@ -1588,6 +1645,7 @@ namespace Microsoft.PowerShell m |= desiredMode; ConsoleControl.SetMode(handle, m); } + // If more characters are typed than you asked, then the next call to ReadConsole will return the // additional characters beyond those you requested. // @@ -1604,10 +1662,10 @@ namespace Microsoft.PowerShell // the empty string. uint keyState = 0; - string restOfLine = null; rawui.ClearKeyCache(); + string s = ""; do { s += ConsoleControl.ReadConsole(handle, initialContent, maxInputLineLength, endOnTab, out keyState); @@ -1688,23 +1746,12 @@ namespace Microsoft.PowerShell while (true); Dbg.Assert( - (s == null && result == ReadLineResult.endedOnBreak) - || (s != null && result != ReadLineResult.endedOnBreak), - "s should only be null if input ended with a break"); - - if (transcribeResult) - { - PostRead(s); - } - else - { - PostRead(); - } - - if (restOfLine != null) - s += restOfLine; + (s == null && result == ReadLineResult.endedOnBreak) + || (s != null && result != ReadLineResult.endedOnBreak), + "s should only be null if input ended with a break"); return s; +#endif } /// @@ -1712,6 +1759,7 @@ namespace Microsoft.PowerShell /// /// the cursor position where 'tab' is hit /// +#if !PORTABLE private char GetCharacterUnderCursor(Coordinates cursorPosition) { Rectangle region = new Rectangle(0, cursorPosition.Y, RawUI.BufferSize.Width - 1, cursorPosition.Y); @@ -1734,6 +1782,7 @@ namespace Microsoft.PowerShell Dbg.Assert(false, "the character at the cursor should be retrieved, never gets to here"); return '\0'; } +#endif /// @@ -1764,29 +1813,23 @@ namespace Microsoft.PowerShell /// /// The Executor instance on which to run any pipelines that are needed to find matches /// - /// - /// - /// - /// - /// Flag which decides if we should look for a user defined ReadLine function - /// - /// - /// /// /// /// null on a break event /// the completed line otherwise /// /// - internal string ReadLineWithTabCompletion(Executor exec, bool useUserDefinedCustomReadLine) + internal string ReadLineWithTabCompletion(Executor exec) { - ConsoleHandle handle = ConsoleControl.GetActiveScreenBufferHandle(); - string input = null; string lastInput = ""; - string lastCompletion = ""; ReadLineResult rlResult = ReadLineResult.endedOnEnter; + +#if !PORTABLE + ConsoleHandle handle = ConsoleControl.GetActiveScreenBufferHandle(); + + string lastCompletion = ""; Size screenBufferSize = RawUI.BufferSize; // Save the cursor position at the end of the prompt string so that we can restore it later to write the @@ -1796,19 +1839,17 @@ namespace Microsoft.PowerShell CommandCompletion commandCompletion = null; string completionInput = null; +#endif do { - if (TryInvokeUserDefinedReadLine(out input, useUserDefinedCustomReadLine)) + if (!ReadFromStdin && TryInvokeUserDefinedReadLine(out input)) { break; } input = ReadLine(true, lastInput, out rlResult, false, false); - Coordinates endOfInputCursorPos = RawUI.CursorPosition; - string completedInput = null; - if (input == null) { break; @@ -1819,6 +1860,13 @@ namespace Microsoft.PowerShell break; } +#if PORTABLE // Portable code only ends on enter (or no input), so tab is not processed + throw new PlatformNotSupportedException("This readline state is unsupported in portable code!"); +#else + + Coordinates endOfInputCursorPos = RawUI.CursorPosition; + string completedInput = null; + if (rlResult == ReadLineResult.endedOnTab || rlResult == ReadLineResult.endedOnShiftTab) { int tabIndex = input.IndexOf(Tab, StringComparison.CurrentCulture); @@ -1828,16 +1876,11 @@ namespace Microsoft.PowerShell int leftover = input.Length - tabIndex - 1; if (leftover > 0) { - // Check if the std input is redirected, e.g. reading from a file - bool isStdInputRedirected = parent.IsStandardInputRedirected && readFromStdin; - if (!isStdInputRedirected) - { - // We are reading from the console - // If the cursor is at the end of a line, there is actually a space character at the cursor's position and when we type tab - // at the end of a line, that space character is replaced by the tab. But when we type tab at the middle of a line, the space - // character at the end is preserved, we should remove that space character because it's not provided by the user. - input = input.Remove(input.Length - 1); - } + // We are reading from the console (not redirected, b/c we don't end on tab when redirected) + // If the cursor is at the end of a line, there is actually a space character at the cursor's position and when we type tab + // at the end of a line, that space character is replaced by the tab. But when we type tab at the middle of a line, the space + // character at the end is preserved, we should remove that space character because it's not provided by the user. + input = input.Remove(input.Length - 1); restOfLine = input.Substring(tabIndex + 1); } input = input.Remove(tabIndex); @@ -1927,6 +1970,7 @@ namespace Microsoft.PowerShell lastInput = completedInput; } +#endif } while (true); @@ -1942,6 +1986,7 @@ namespace Microsoft.PowerShell return input; } +#if !PORTABLE private void SendLeftArrows(int length) { var inputs = new ConsoleControl.INPUT[length * 2]; @@ -1971,6 +2016,7 @@ namespace Microsoft.PowerShell ConsoleControl.MimicKeyPress(inputs); } +#endif private CommandCompletion GetNewCompletionResults(string input) { @@ -2011,44 +2057,43 @@ namespace Microsoft.PowerShell } const string CustomReadlineCommand = "PSConsoleHostReadLine"; - private bool TryInvokeUserDefinedReadLine(out string input, bool useUserDefinedCustomReadLine) + private bool TryInvokeUserDefinedReadLine(out string input) { // We're using GetCommands instead of GetCommand so we don't auto-load a module should the command exist, but isn't loaded. // The idea is that if someone hasn't defined the command (say because they started -noprofile), we shouldn't auto-load // this function. - // If we need to look for user defined custom readline command, then we need to wait for Runspace to be created. - if (useUserDefinedCustomReadLine) + var runspace = this.parent.LocalRunspace; + if (runspace != null && + runspace.Engine.Context.EngineIntrinsics.InvokeCommand. + GetCommands(CustomReadlineCommand, + CommandTypes.Function | CommandTypes.Cmdlet, nameIsPattern: false).Any()) { - var runspace = this.parent.LocalRunspace; - if (runspace != null - && runspace.Engine.Context.EngineIntrinsics.InvokeCommand.GetCommands(CustomReadlineCommand, CommandTypes.Function | CommandTypes.Cmdlet, nameIsPattern: false).Any()) + try { - try + PowerShell ps; + if ((runspace.ExecutionContext.EngineHostInterface.NestedPromptCount > 0) && + (Runspace.DefaultRunspace != null)) { - PowerShell ps; - if ((runspace.ExecutionContext.EngineHostInterface.NestedPromptCount > 0) && (Runspace.DefaultRunspace != null)) - { - ps = PowerShell.Create(RunspaceMode.CurrentRunspace); - } - else - { - ps = PowerShell.Create(); - ps.Runspace = runspace; - } + ps = PowerShell.Create(RunspaceMode.CurrentRunspace); + } + else + { + ps = PowerShell.Create(); + ps.Runspace = runspace; + } - var result = ps.AddCommand(CustomReadlineCommand).Invoke(); - if (result.Count == 1) - { - input = PSObject.Base(result[0]) as string; - return true; - } - } - catch (Exception e) + var result = ps.AddCommand(CustomReadlineCommand).Invoke(); + if (result.Count == 1) { - CommandProcessorBase.CheckForSevereException(e); + input = PSObject.Base(result[0]) as string; + return true; } } + catch (Exception e) + { + CommandProcessorBase.CheckForSevereException(e); + } } input = null; @@ -2061,7 +2106,6 @@ namespace Microsoft.PowerShell private object instanceLock = new object(); - private bool readFromStdin; private bool noPrompt; //If this is true, class throws on read or prompt method which require @@ -2086,7 +2130,6 @@ namespace Microsoft.PowerShell // this is a test hook for the ConsoleInteractiveTestTool, which sets this field to true. private bool isInteractiveTestToolListening; - private bool isTestingShiftTab; // This instance data is "read-only" and need not have access serialized. diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/Executor.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/Executor.cs index 32670dc51f..9ba4e829f7 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/Executor.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/Executor.cs @@ -215,11 +215,11 @@ namespace Microsoft.PowerShell PipelineFinishedWaitHandle waiterThereIsAFlyInMySoup = new PipelineFinishedWaitHandle(tempPipeline); tempPipeline.InvokeAsync(); - if ((options & ExecutionOptions.ReadInputObjects) > 0 && parent.IsStandardInputRedirected) + if ((options & ExecutionOptions.ReadInputObjects) > 0 && Console.IsInputRedirected) { // read input objects from stdin - WrappedDeserializer des = new WrappedDeserializer(parent.InputFormat, "Input", parent.StandardInReader); + WrappedDeserializer des = new WrappedDeserializer(parent.InputFormat, "Input", Console.In); while (!des.AtEnd) { object o = des.Deserialize(); diff --git a/src/Microsoft.PowerShell.ConsoleHost/project.json b/src/Microsoft.PowerShell.ConsoleHost/project.json index 76a1494a5d..d4e1d0b7b6 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/project.json +++ b/src/Microsoft.PowerShell.ConsoleHost/project.json @@ -1,7 +1,7 @@ { "name": "Microsoft.PowerShell.ConsoleHost", "version": "1.0.0-*", - "description": "Full Windows PowerShell Host (requires native host)", + "description": "PowerShell Host", "authors": [ "sevoroby", "andschwa" ], "compilationOptions": { @@ -10,6 +10,7 @@ }, "dependencies": { + "Microsoft.PowerShell.PSReadLine": "1.0.0-*", "Microsoft.PowerShell.Commands.Management": "1.0.0-*", "Microsoft.PowerShell.Commands.Utility": "1.0.0-*" }, @@ -21,6 +22,17 @@ ], "frameworks": { + "netstandard1.5": { + "imports": [ "dnxcore50", "portable-net45+win8" ], + "compilationOptions": { + "define": [ "CORECLR", "PORTABLE" ] + }, + "dependencies": { + "Newtonsoft.Json": "8.0.2", + "System.Xml.XDocument": "4.0.11-rc2-24103", + "System.IO.MemoryMappedFiles": "4.0.0-rc2-24103" + } + }, "net451": { } }, diff --git a/src/Microsoft.PowerShell.ConsoleHost/singleshell/installer/EngineInstaller.cs b/src/Microsoft.PowerShell.ConsoleHost/singleshell/installer/EngineInstaller.cs index bcc5467e64..3e7971855f 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/singleshell/installer/EngineInstaller.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/singleshell/installer/EngineInstaller.cs @@ -1,3 +1,4 @@ +#if !PORTABLE /********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ @@ -86,3 +87,4 @@ namespace Microsoft.PowerShell } } } +#endif diff --git a/src/Microsoft.PowerShell.ConsoleHost/singleshell/installer/MshHostMshSnapin.cs b/src/Microsoft.PowerShell.ConsoleHost/singleshell/installer/MshHostMshSnapin.cs index 615d5dfc26..0b9a0f65e2 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/singleshell/installer/MshHostMshSnapin.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/singleshell/installer/MshHostMshSnapin.cs @@ -1,3 +1,4 @@ +#if !PORTABLE /********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ @@ -92,3 +93,4 @@ namespace Microsoft.PowerShell } } } +#endif diff --git a/src/Microsoft.PowerShell.CoreConsoleHost/AssemblyInfo.cs b/src/Microsoft.PowerShell.CoreConsoleHost/AssemblyInfo.cs deleted file mode 100644 index 3e4b06af93..0000000000 --- a/src/Microsoft.PowerShell.CoreConsoleHost/AssemblyInfo.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Runtime.CompilerServices; -using System.Reflection; -using System.Resources; - -[assembly:InternalsVisibleTo("powershell-tests")] - -[assembly:AssemblyCulture("")] -[assembly:NeutralResourcesLanguage("en-US")] diff --git a/src/Microsoft.PowerShell.CoreConsoleHost/README.md b/src/Microsoft.PowerShell.CoreConsoleHost/README.md deleted file mode 100644 index 9c8e1e197a..0000000000 --- a/src/Microsoft.PowerShell.CoreConsoleHost/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# Core PowerShell Host - -This host is based off the infamous `host06` example. It relies only on managed -code, and is cross-platform. It needs a fair bit of work, but it loads -profiles, has tab-completion, and can be used to debug PowerShell scripts. - -## Executable - -The `bin/powershell[.exe]` executable for Core PowerShell is built by this -project, as it is the top dependency of the graph, and has `emitEntryPoint: -true`, meaning a native executable is produced automatically by CLI (no need to -own a separate native host). It is also the project that deploys the `ps1xml` -types and formatting files, as well as the default profile and the included -PowerShell modules. - -Note that many of these should probably live with System.Management.Automation, -but we're waiting on a bug fix from CLI to move them. - -## update-content.sh - -This script is used to update our current tree's files with those that live in -`src/monad`. We only need to update them when new changes are merged from -Source Depot, and the build scripts are much simpler without this logic. With -the files living here, the `content` key in the `project.json` handles the -deployment for us in a cross-platform manner. diff --git a/src/Microsoft.PowerShell.CoreConsoleHost/host.cs b/src/Microsoft.PowerShell.CoreConsoleHost/host.cs deleted file mode 100644 index c987abac76..0000000000 --- a/src/Microsoft.PowerShell.CoreConsoleHost/host.cs +++ /dev/null @@ -1,218 +0,0 @@ -namespace Microsoft.PowerShell.CoreConsoleHost -{ - using System; - using System.Globalization; - using System.Management.Automation.Host; - using System.Management.Automation.Runspaces; - - /// - /// This is a sample implementation of the PSHost abstract class for - /// console applications. Not all members are implemented. Those that - /// are not implemented throw a NotImplementedException exception or - /// return nothing. - /// - internal class MyHost : PSHost, IHostSupportsInteractiveSession - { - public MyHost(Listener Listener) - { - this.Listener = Listener; - this.myHostUserInterface = new MyHostUserInterface(Listener.HasUI, Listener.Interactive); - } - - /// - /// A reference to the PSHost implementation. - /// - private Listener Listener; - - /// - /// The culture information of the thread that created - /// this object. - /// - private CultureInfo originalCultureInfo = CultureInfo.CurrentCulture; - - /// - /// The UI culture information of the thread that created - /// this object. - /// - private CultureInfo originalUICultureInfo = CultureInfo.CurrentCulture; - - /// - /// The identifier of this PSHost implementation. - /// - private static Guid instanceId = Guid.NewGuid(); - - /// - /// A reference to the implementation of the PSHostUserInterface - /// class for this application. - /// - private MyHostUserInterface myHostUserInterface; - - /// - /// A reference to the runspace used to start an interactive session. - /// - public Runspace pushedRunspace = null; - - - /// - /// Gets the culture information to use. This implementation - /// returns a snapshot of the culture information of the thread - /// that created this object. - /// - public override CultureInfo CurrentCulture - { - get { return this.originalCultureInfo; } - } - - /// - /// Gets the UI culture information to use. This implementation - /// returns a snapshot of the UI culture information of the thread - /// that created this object. - /// - public override CultureInfo CurrentUICulture - { - get { return this.originalUICultureInfo; } - } - - /// - /// Gets an identifier for this host. This implementation always - /// returns the GUID allocated at instantiation time. - /// - public override Guid InstanceId - { - get { return instanceId; } - } - - /// - /// Gets a string that contains the name of this host implementation. - /// Keep in mind that this string may be used by script writers to - /// identify when your host is being used. - /// - public override string Name - { - get { return "CoreConsoleHost"; } - } - - /// - /// Gets an instance of the implementation of the PSHostUserInterface - /// class for this application. This instance is allocated once at startup time - /// and returned every time thereafter. - /// - public override PSHostUserInterface UI - { - get { return this.myHostUserInterface; } - } - - /// - /// Gets the version object for this application. Typically this - /// should match the version resource in the application. - /// - public override Version Version - { - get { return new Version(1, 0, 0, 0); } - } - -#region IHostSupportsInteractiveSession Properties - - /// - /// Gets a value indicating whether a request - /// to open a PSSession has been made. - /// - public bool IsRunspacePushed - { - get { return this.pushedRunspace != null; } - } - - /// - /// Gets or sets the runspace used by the PSSession. - /// - public Runspace Runspace - { - get { return this.Listener.myRunSpace; } - internal set { this.Listener.myRunSpace = value; } - } -#endregion IHostSupportsInteractiveSession Properties - - /// - /// This API Instructs the host to interrupt the currently running - /// pipeline and start a new nested input loop. In this example this - /// functionality is not needed so the method throws a - /// NotImplementedException exception. - /// - public override void EnterNestedPrompt() - { - throw new NotImplementedException( - "The method or operation is not implemented."); - } - - /// - /// This API instructs the host to exit the currently running input loop. - /// In this example this functionality is not needed so the method - /// throws a NotImplementedException exception. - /// - public override void ExitNestedPrompt() - { - throw new NotImplementedException( - "The method or operation is not implemented."); - } - - /// - /// This API is called before an external application process is - /// started. Typically it is used to save state so that the parent - /// can restore state that has been modified by a child process (after - /// the child exits). In this example this functionality is not - /// needed so the method returns nothing. - /// - public override void NotifyBeginApplication() - { - return; - } - - /// - /// This API is called after an external application process finishes. - /// Typically it is used to restore state that a child process has - /// altered. In this example, this functionality is not needed so - /// the method returns nothing. - /// - public override void NotifyEndApplication() - { - return; - } - - /// - /// Indicate to the host application that exit has - /// been requested. Pass the exit code that the host - /// application should use when exiting the process. - /// - /// The exit code that the - /// host application should use. - public override void SetShouldExit(int exitCode) - { - this.Listener.ShouldExit = true; - this.Listener.ExitCode = exitCode; - } - -#region IHostSupportsInteractiveSession Methods - - /// - /// Requests to close a PSSession. - /// - public void PopRunspace() - { - Runspace = this.pushedRunspace; - this.pushedRunspace = null; - } - - /// - /// Requests to open a PSSession. - /// - /// Runspace to use. - public void PushRunspace(Runspace runspace) - { - this.pushedRunspace = Runspace; - Runspace = runspace; - } - -#endregion IHostSupportsInteractiveSession Methods - } -} - diff --git a/src/Microsoft.PowerShell.CoreConsoleHost/main.cs b/src/Microsoft.PowerShell.CoreConsoleHost/main.cs deleted file mode 100644 index 94c0df6c1d..0000000000 --- a/src/Microsoft.PowerShell.CoreConsoleHost/main.cs +++ /dev/null @@ -1,857 +0,0 @@ -namespace Microsoft.PowerShell.CoreConsoleHost -{ - using System; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Management.Automation; - using System.Management.Automation.Host; - using System.Management.Automation.Runspaces; - using System.Text; - using System.IO; - using System.Runtime.InteropServices; - using System.Linq; - using PowerShell = System.Management.Automation.PowerShell; - - public static class Program - { - private enum Options {Help, File, Command, NoProfile, NonInteractive, EncodedCommand, Invalid}; - private class CommandOptions - { - public Options opt; - public string optString; - - public CommandOptions(Options o, string s) - { - opt = o; - optString = s; - } - } - - private static CommandOptions[] commandOptions = new CommandOptions[] - { - new CommandOptions(Options.Help, "help"), - new CommandOptions(Options.Help, "?"), - new CommandOptions(Options.File, "file"), - new CommandOptions(Options.Command, "command"), - new CommandOptions(Options.NoProfile, "noprofile"), - new CommandOptions(Options.NonInteractive, "noninteractive"), - new CommandOptions(Options.EncodedCommand, "encodedcommand") - }; - - /// - /// Creates and initiates the listener instance. - /// - public static void Main(string[] args) - { - // Setup the Assembly Load Context, which Core PowerShell uses to - // analyze the libraries for types, functions, cmdlets, etc. and - // provide the ability to load assemblies by file path. Doing this - // here eliminates the need for a custom native host. - PowerShellAssemblyLoadContextInitializer.SetPowerShellAssemblyLoadContext(string.Empty); - - - // Argument parsing - string initialScript = null; - bool loadProfiles = true; - bool interactive = true; - - if (args.Length > 0) - { - for (int i = 0; i < args.Length; ++i) - { - string arg = args[i]; - bool hasNext = (i+1) < args.Length; - string nextArg = hasNext ? args[i+1] : string.Empty; - - // options start with "-" or "--" - - string option; - if (arg.StartsWith("-")) - { - if (arg.StartsWith("--")) - { - option = arg.Remove(0, 2); - } - else - { - option = arg.Remove(0, 1); - } - - Options bestOption = FindOption(option); - - switch (bestOption) - { - case Options.Help: - Console.WriteLine(@" -usage: powershell[.exe] [ (--help | -h | -?) ] - [ (--file | -f) ] - [