From 9f475a7ef3298b4a674056b59c1d3835a6c4b589 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Sat, 20 Oct 2018 10:10:11 +0800 Subject: [PATCH 1/4] enable set-location -literalpath to work with folders named "-" and "+" --- .../commands/management/Navigation.cs | 2 +- .../engine/PathInterfaces.cs | 44 ++++++++++++++++++ .../engine/SessionStateLocationAPIs.cs | 45 ++++++++++++++++++- .../Set-Location.Tests.ps1 | 13 ++++++ 4 files changed, 101 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Navigation.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Navigation.cs index a58d0963d80..0df84608805 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Navigation.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Navigation.cs @@ -801,7 +801,7 @@ protected override void ProcessRecord() Path = SessionState.Internal.GetSingleProvider(Commands.FileSystemProvider.ProviderName).Home; } - result = SessionState.Path.SetLocation(Path, CmdletProviderContext); + result = SessionState.Path.SetLocation(Path, CmdletProviderContext, ParameterSetName == literalPathSet); } catch (PSNotSupportedException notSupported) { diff --git a/src/System.Management.Automation/engine/PathInterfaces.cs b/src/System.Management.Automation/engine/PathInterfaces.cs index eafa2dc5762..3db667b079f 100644 --- a/src/System.Management.Automation/engine/PathInterfaces.cs +++ b/src/System.Management.Automation/engine/PathInterfaces.cs @@ -190,6 +190,50 @@ internal PathInfo SetLocation(string path, CmdletProviderContext context) return _sessionState.SetLocation(path, context); } // SetLocation + /// + /// Changes the current location to the specified path. + /// + /// + /// The path to change the location to. This can be either a drive-relative or provider-relative + /// path. It cannot be a provider-internal path. + /// + /// + /// The context under which the command is running. + /// + /// + /// Indicates if the path is a literal path. + /// + /// + /// The path of the new current location. + /// + /// + /// If is null. + /// + /// + /// If does not exist, is not a container, or + /// resolved to multiple containers. + /// + /// + /// If refers to a provider that does not exist. + /// + /// + /// If refers to a drive that does not exist. + /// + /// + /// If the provider associated with threw an + /// exception. + /// + internal PathInfo SetLocation(string path, CmdletProviderContext context, bool literalPath) + { + Dbg.Diagnostics.Assert( + _sessionState != null, + "The only constructor for this class should always set the sessionState field"); + + // Parameter validation is done in the session state object + + return _sessionState.SetLocation(path, context, literalPath); + } // SetLocation + /// /// Determines if the specified path is the current location or a parent of the current location. /// diff --git a/src/System.Management.Automation/engine/SessionStateLocationAPIs.cs b/src/System.Management.Automation/engine/SessionStateLocationAPIs.cs index fcc850b1fbf..175a4f8a95b 100644 --- a/src/System.Management.Automation/engine/SessionStateLocationAPIs.cs +++ b/src/System.Management.Automation/engine/SessionStateLocationAPIs.cs @@ -194,6 +194,47 @@ internal PathInfo SetLocation(string path) /// If the could not be resolved. /// internal PathInfo SetLocation(string path, CmdletProviderContext context) + { + return SetLocation(path, context, literalPath: false); + } + + /// + /// Changes the current working directory to the path specified + /// + /// + /// The path of the new current working directory + /// + /// + /// The context the provider uses when performing the operation. + /// + /// + /// Indicate if the path is a literal path. + /// + /// + /// The PathInfo object representing the path of the location + /// that was set. + /// + /// + /// If is null. + /// + /// + /// If does not exist, is not a container, or + /// resolved to multiple containers. + /// + /// + /// If refers to a provider that does not exist. + /// + /// + /// If refers to a drive that does not exist. + /// + /// + /// If the provider associated with threw an + /// exception. + /// + /// + /// If the could not be resolved. + /// + internal PathInfo SetLocation(string path, CmdletProviderContext context, bool literalPath) { if (path == null) { @@ -208,14 +249,14 @@ internal PathInfo SetLocation(string path, CmdletProviderContext context) switch (originalPath) { - case string originalPathSwitch when originalPathSwitch.Equals("-", StringComparison.OrdinalIgnoreCase): + case string originalPathSwitch when !literalPath && originalPathSwitch.Equals("-", StringComparison.OrdinalIgnoreCase): if (_setLocationHistory.UndoCount <= 0) { throw new InvalidOperationException(SessionStateStrings.LocationUndoStackIsEmpty); } path = _setLocationHistory.Undo(this.CurrentLocation).Path; break; - case string originalPathSwitch when originalPathSwitch.Equals("+", StringComparison.OrdinalIgnoreCase): + case string originalPathSwitch when !literalPath && originalPathSwitch.Equals("+", StringComparison.OrdinalIgnoreCase): if (_setLocationHistory.RedoCount <= 0) { throw new InvalidOperationException(SessionStateStrings.LocationRedoStackIsEmpty); diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Location.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Location.Tests.ps1 index 8228e79bb31..5824419f3b1 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Location.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Set-Location.Tests.ps1 @@ -165,6 +165,19 @@ Describe "Set-Location" -Tags "CI" { } } + It 'Should nativate to literal path ""' -TestCases @( + @{ path = "-" }, + @{ path = "+" } + ) { + param($path) + + Set-Location $TestDrive + $literalPath = Join-Path $TestDrive $path + New-Item -ItemType Directory -Path $literalPath + Set-Location -LiteralPath $path + (Get-Location).Path | Should -BeExactly $literalPath + } + Context 'Test the LocationChangedAction event handler' { AfterEach { From 139600a9eebae9047e94f6ee8dfcdd86c87e9d92 Mon Sep 17 00:00:00 2001 From: Ilya Date: Mon, 22 Oct 2018 00:58:26 +0800 Subject: [PATCH 2/4] Update src/System.Management.Automation/engine/SessionStateLocationAPIs.cs Co-Authored-By: SteveL-MSFT --- .../engine/SessionStateLocationAPIs.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Management.Automation/engine/SessionStateLocationAPIs.cs b/src/System.Management.Automation/engine/SessionStateLocationAPIs.cs index 175a4f8a95b..d64e6a74f67 100644 --- a/src/System.Management.Automation/engine/SessionStateLocationAPIs.cs +++ b/src/System.Management.Automation/engine/SessionStateLocationAPIs.cs @@ -249,7 +249,7 @@ internal PathInfo SetLocation(string path, CmdletProviderContext context, bool l switch (originalPath) { - case string originalPathSwitch when !literalPath && originalPathSwitch.Equals("-", StringComparison.OrdinalIgnoreCase): + case string originalPathSwitch when !literalPath && originalPathSwitch.Equals("-", StringComparison.Ordinal): if (_setLocationHistory.UndoCount <= 0) { throw new InvalidOperationException(SessionStateStrings.LocationUndoStackIsEmpty); From 3361fe03fec0791cf2816f1be73b283ddd15e474 Mon Sep 17 00:00:00 2001 From: Ilya Date: Mon, 22 Oct 2018 00:58:36 +0800 Subject: [PATCH 3/4] Update src/System.Management.Automation/engine/SessionStateLocationAPIs.cs Co-Authored-By: SteveL-MSFT --- .../engine/SessionStateLocationAPIs.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Management.Automation/engine/SessionStateLocationAPIs.cs b/src/System.Management.Automation/engine/SessionStateLocationAPIs.cs index d64e6a74f67..5b67a503c4c 100644 --- a/src/System.Management.Automation/engine/SessionStateLocationAPIs.cs +++ b/src/System.Management.Automation/engine/SessionStateLocationAPIs.cs @@ -256,7 +256,7 @@ internal PathInfo SetLocation(string path, CmdletProviderContext context, bool l } path = _setLocationHistory.Undo(this.CurrentLocation).Path; break; - case string originalPathSwitch when !literalPath && originalPathSwitch.Equals("+", StringComparison.OrdinalIgnoreCase): + case string originalPathSwitch when !literalPath && originalPathSwitch.Equals("+", StringComparison.Ordinal): if (_setLocationHistory.RedoCount <= 0) { throw new InvalidOperationException(SessionStateStrings.LocationRedoStackIsEmpty); From 39165c864d0fd3350fcb3a401f8ebd57a248e3ac Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Sun, 21 Oct 2018 10:12:16 -0700 Subject: [PATCH 4/4] [feature] address codefactor issues --- .../engine/SessionStateLocationAPIs.cs | 60 +++++-------------- 1 file changed, 14 insertions(+), 46 deletions(-) diff --git a/src/System.Management.Automation/engine/SessionStateLocationAPIs.cs b/src/System.Management.Automation/engine/SessionStateLocationAPIs.cs index 5b67a503c4c..11542da0018 100644 --- a/src/System.Management.Automation/engine/SessionStateLocationAPIs.cs +++ b/src/System.Management.Automation/engine/SessionStateLocationAPIs.cs @@ -13,7 +13,7 @@ namespace System.Management.Automation { /// - /// Holds the state of a Monad Shell session + /// Holds the state of a Monad Shell session. /// internal sealed partial class SessionStateInternal { @@ -79,7 +79,6 @@ internal PathInfo GetNamespaceCurrentLocation(string namespaceID) } // If namespace ID is empty, we will use the current working drive - PSDriveInfo drive = null; if (namespaceID.Length == 0) @@ -89,7 +88,6 @@ internal PathInfo GetNamespaceCurrentLocation(string namespaceID) else { // First check to see if the provider exists - ProvidersCurrentWorkingDrive.TryGetValue(GetSingleProvider(namespaceID), out drive); } @@ -107,7 +105,6 @@ internal PathInfo GetNamespaceCurrentLocation(string namespaceID) context.Drive = drive; // Now make the namespace specific path - string path = null; if (drive.Hidden) @@ -129,10 +126,10 @@ internal PathInfo GetNamespaceCurrentLocation(string namespaceID) } // GetNamespaceCurrentLocation /// - /// Changes the current working directory to the path specified + /// Changes the current working directory to the path specified. /// /// - /// The path of the new current working directory + /// The path of the new current working directory. /// /// /// The PathInfo object representing the path of the location @@ -199,10 +196,10 @@ internal PathInfo SetLocation(string path, CmdletProviderContext context) } /// - /// Changes the current working directory to the path specified + /// Changes the current working directory to the path specified. /// /// - /// The path of the new current working directory + /// The path of the new current working directory. /// /// /// The context the provider uses when performing the operation. @@ -254,6 +251,7 @@ internal PathInfo SetLocation(string path, CmdletProviderContext context, bool l { throw new InvalidOperationException(SessionStateStrings.LocationUndoStackIsEmpty); } + path = _setLocationHistory.Undo(this.CurrentLocation).Path; break; case string originalPathSwitch when !literalPath && originalPathSwitch.Equals("+", StringComparison.Ordinal): @@ -261,6 +259,7 @@ internal PathInfo SetLocation(string path, CmdletProviderContext context, bool l { throw new InvalidOperationException(SessionStateStrings.LocationRedoStackIsEmpty); } + path = _setLocationHistory.Redo(this.CurrentLocation).Path; break; default: @@ -282,7 +281,6 @@ internal PathInfo SetLocation(string path, CmdletProviderContext context, bool l // The path is a provider-direct path so use the current // provider and its hidden drive but don't modify the path // at all. - provider = CurrentLocation.Provider; CurrentDrive = provider.HiddenDrive; } @@ -295,7 +293,6 @@ internal PathInfo SetLocation(string path, CmdletProviderContext context, bool l { // See if the path is a relative or absolute // path. - if (Globber.IsAbsolutePath(path, out driveName)) { // Since the path is an absolute path @@ -310,7 +307,7 @@ internal PathInfo SetLocation(string path, CmdletProviderContext context, bool l string colonTerminatedVolume = CurrentDrive.Name + ':'; if (CurrentDrive.VolumeSeparatedByColon && (path.Length == colonTerminatedVolume.Length)) { - path = Path.Combine((colonTerminatedVolume + Path.DirectorySeparatorChar), CurrentDrive.CurrentLocation); + path = Path.Combine(colonTerminatedVolume + Path.DirectorySeparatorChar, CurrentDrive.CurrentLocation); } // Now that the current working drive is set, @@ -353,11 +350,10 @@ internal PathInfo SetLocation(string path, CmdletProviderContext context, bool l { throw; } - catch (Exception) // Catch-all OK, 3rd party callout + catch (Exception) { // Reset the drive to the previous drive and // then rethrow the error - CurrentDrive = previousWorkingDrive; throw; } @@ -366,7 +362,6 @@ internal PathInfo SetLocation(string path, CmdletProviderContext context, bool l { // Set the current working drive back to the previous // one in case it was changed. - CurrentDrive = previousWorkingDrive; throw @@ -377,7 +372,6 @@ internal PathInfo SetLocation(string path, CmdletProviderContext context, bool l } // We allow globbing the location as long as it only resolves a single container. - bool foundContainer = false; bool pathIsContainer = false; bool pathIsProviderQualifiedPath = false; @@ -398,12 +392,11 @@ internal PathInfo SetLocation(string path, CmdletProviderContext context, bool l { // The path should be the provider-qualified path without the provider ID // or :: - string providerInternalPath = LocationGlobber.RemoveProviderQualifier(resolvedPath.Path); try { - currentPath = NormalizeRelativePath(GetSingleProvider(providerName), providerInternalPath, String.Empty, normalizePathContext); + currentPath = NormalizeRelativePath(GetSingleProvider(providerName), providerInternalPath, string.Empty, normalizePathContext); } catch (NotSupportedException) { @@ -422,11 +415,10 @@ internal PathInfo SetLocation(string path, CmdletProviderContext context, bool l { throw; } - catch (Exception) // Catch-all OK, 3rd party callout + catch (Exception) { // Reset the drive to the previous drive and // then rethrow the error - CurrentDrive = previousWorkingDrive; throw; } @@ -454,23 +446,20 @@ internal PathInfo SetLocation(string path, CmdletProviderContext context, bool l { throw; } - catch (Exception) // Catch-all OK, 3rd party callout + catch (Exception) { // Reset the drive to the previous drive and // then rethrow the error - CurrentDrive = previousWorkingDrive; throw; } } // Now see if there was errors while normalizing the path - if (normalizePathContext.HasErrors()) { // Set the current working drive back to the previous // one in case it was changed. - CurrentDrive = previousWorkingDrive; normalizePathContext.ThrowFirstErrorOrDoNothing(); @@ -481,8 +470,6 @@ internal PathInfo SetLocation(string path, CmdletProviderContext context, bool l normalizePathContext.RemoveStopReferral(); } - // Check to see if the path is a container - bool isContainer = false; CmdletProviderContext itemContainerContext = @@ -500,7 +487,6 @@ internal PathInfo SetLocation(string path, CmdletProviderContext context, bool l { // Set the current working drive back to the previous // one in case it was changed. - CurrentDrive = previousWorkingDrive; itemContainerContext.ThrowFirstErrorOrDoNothing(); @@ -513,7 +499,6 @@ internal PathInfo SetLocation(string path, CmdletProviderContext context, bool l // Treat this as a container because providers that only // support the ContainerCmdletProvider interface are really // containers at their root. - isContainer = true; } } @@ -527,10 +512,8 @@ internal PathInfo SetLocation(string path, CmdletProviderContext context, bool l if (foundContainer) { // The path resolved to more than one container - // Set the current working drive back to the previous // one in case it was changed. - CurrentDrive = previousWorkingDrive; throw @@ -561,7 +544,6 @@ internal PathInfo SetLocation(string path, CmdletProviderContext context, bool l { // Remove the root slash since it is implied that the // current working directory is relative to the root. - if (!LocationGlobber.IsProviderDirectPath(path) && path.StartsWith(StringLiterals.DefaultPathSeparatorString, StringComparison.CurrentCulture) && !pathIsProviderQualifiedPath) @@ -579,7 +561,6 @@ internal PathInfo SetLocation(string path, CmdletProviderContext context, bool l { // Set the current working drive back to the previous // one in case it was changed. - CurrentDrive = previousWorkingDrive; throw @@ -591,12 +572,10 @@ internal PathInfo SetLocation(string path, CmdletProviderContext context, bool l // Now make sure the current drive is set in the provider's // current working drive hashtable - ProvidersCurrentWorkingDrive[CurrentDrive.Provider] = CurrentDrive; // Set the $PWD variable to the new location - this.SetVariable(SpecialVariables.PWDVarPath, this.CurrentLocation, false, true, CommandOrigin.Internal); // If an action has been defined for location changes, invoke it now. @@ -687,12 +666,10 @@ internal bool IsCurrentLocationOrAncestor(string path, CmdletProviderContext con // Check to see if the path that was specified is within the current // working drive - if (drive == CurrentDrive) { // The path needs to be normalized to get rid of relative path tokens // so they don't interfere with our path comparisons below - CmdletProviderContext normalizePathContext = new CmdletProviderContext(context); @@ -730,7 +707,6 @@ CmdletProviderContext normalizePathContext s_tracer.WriteLine("Provider path = {0}", providerSpecificPath); // Get the current working directory provider specific path - PSDriveInfo currentWorkingDrive = null; ProviderInfo currentDriveProvider = null; @@ -751,7 +727,6 @@ CmdletProviderContext normalizePathContext // See if the path is the current working directory or a parent // of the current working directory - s_tracer.WriteLine( "Comparing {0} to {1}", providerSpecificPath, @@ -761,7 +736,6 @@ CmdletProviderContext normalizePathContext { // The path is the current working directory so // return true - s_tracer.WriteLine("The path is the current working directory"); result = true; @@ -770,7 +744,6 @@ CmdletProviderContext normalizePathContext { // Check to see if the specified path is a parent // of the current working directory - string lockedDirectory = currentWorkingPath; while (lockedDirectory.Length > 0) @@ -779,7 +752,6 @@ CmdletProviderContext normalizePathContext // as it can even if that means it has to traverse higher // than the mount point for this drive. That is // why we are passing the empty string as the root here. - lockedDirectory = GetParentPath( drive.Provider, @@ -796,7 +768,6 @@ CmdletProviderContext normalizePathContext { // The path is a parent of the current working // directory - s_tracer.WriteLine( "The path is a parent of the current working directory: {0}", lockedDirectory); @@ -825,13 +796,13 @@ CmdletProviderContext normalizePathContext private readonly HistoryStack _setLocationHistory; /// - /// A stack of the most recently pushed locations + /// A stack of the most recently pushed locations. /// private Dictionary> _workingLocationStack; private const string startingDefaultStackName = "default"; /// - /// The name of the default location stack + /// The name of the default location stack. /// private string _defaultStackName = startingDefaultStackName; @@ -928,7 +899,6 @@ internal PathInfo PopLocation(string stackName) if (WildcardPattern.ContainsWildcardCharacters(stackName)) { // Need to glob the stack name, but it can only glob to a single. - bool haveMatch = false; WildcardPattern stackNamePattern = @@ -989,7 +959,6 @@ internal PathInfo PopLocation(string stackName) { // Remove the stack from the stack list if it // no longer contains any paths. - _workingLocationStack.Remove(stackName); } } @@ -1031,7 +1000,6 @@ internal PathInfoStack LocationStack(string stackName) { // If the request was for the default stack, but it doesn't // yet exist, create a dummy stack and return it. - if (String.Equals( stackName, startingDefaultStackName,