From 88792ed6bbdad4116c261abf94d453e860d890af Mon Sep 17 00:00:00 2001 From: Marco Rossignoli Date: Tue, 28 May 2019 20:14:05 +0200 Subject: [PATCH] Expose Path.TrimEndingDirectorySeparator (dotnet/coreclr#20805) * expose TrimEndingDirectorySeparator * address PR feedback * updates * address PR feedback * fix comment Commit migrated from https://github.com/dotnet/coreclr/commit/e44730085c65ddb98dad1a3059b4e8f7a1ae33ea --- .../src/System/AppContext.CoreCLR.cs | 2 +- .../Win32/SafeHandles/SafeFileHandle.Unix.cs | 2 +- .../src/System/IO/Path.Windows.cs | 33 ++++++++----------- .../System.Private.CoreLib/src/System/IO/Path.cs | 38 +++++++++++++++++++--- .../src/System/IO/PathInternal.cs | 18 +--------- 5 files changed, 50 insertions(+), 43 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/AppContext.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/AppContext.CoreCLR.cs index 93bef4e..84313e3 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/AppContext.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/AppContext.CoreCLR.cs @@ -21,7 +21,7 @@ namespace System { // Fallback path for hosts that do not set APP_CONTEXT_BASE_DIRECTORY explicitly string? directory = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location); - if (directory != null && !PathInternal.EndsInDirectorySeparator(directory)) + if (directory != null && !Path.EndsInDirectorySeparator(directory)) directory += Path.DirectorySeparatorChar; return directory; } diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs index 602537a..7adc7d0 100644 --- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs @@ -52,7 +52,7 @@ namespace Microsoft.Win32.SafeHandles bool isDirectory = (error.Error == Interop.Error.ENOENT) && ((flags & Interop.Sys.OpenFlags.O_CREAT) != 0 - || !DirectoryExists(Path.GetDirectoryName(PathInternal.TrimEndingDirectorySeparator(path!))!)); + || !DirectoryExists(Path.GetDirectoryName(Path.TrimEndingDirectorySeparator(path!))!)); Interop.CheckIo( error.Error, diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs index 71a986e..16bd3b4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs @@ -248,31 +248,26 @@ namespace System.IO if (root.Length == 0) return root; - int offset = GetUncRootLength(path); - if (offset >= 0) + // Cut from "\\?\UNC\Server\Share" to "Server\Share" + // Cut from "\\Server\Share" to "Server\Share" + int startOffset = GetUncRootLength(path); + if (startOffset == -1) { - // Cut from "\\?\UNC\Server\Share" to "Server\Share" - // Cut from "\\Server\Share" to "Server\Share" - return TrimEndingDirectorySeparator(root.Slice(offset)); - } - else if (PathInternal.IsDevice(path)) - { - return TrimEndingDirectorySeparator(root.Slice(4)); // Cut from "\\?\C:\" to "C:" + if (PathInternal.IsDevice(path)) + { + startOffset = 4; // Cut from "\\?\C:\" to "C:" + } + else + { + startOffset = 0; // e.g. "C:" + } } - return TrimEndingDirectorySeparator(root); // e.g. "C:" + ReadOnlySpan pathToTrim = root.Slice(startOffset); + return Path.EndsInDirectorySeparator(pathToTrim) ? pathToTrim.Slice(0, pathToTrim.Length - 1) : pathToTrim; } /// - /// Trims the ending directory separator if present. - /// - /// - internal static ReadOnlySpan TrimEndingDirectorySeparator(ReadOnlySpan path) => - PathInternal.EndsInDirectorySeparator(path) ? - path.Slice(0, path.Length - 1) : - path; - - /// /// Returns offset as -1 if the path is not in Unc format, otherwise returns the root length. /// /// diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs index b0c3df3..7412eb6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs @@ -541,7 +541,7 @@ namespace System.IO return true; } - bool needsSeparator = !(PathInternal.EndsInDirectorySeparator(path1) || PathInternal.StartsWithDirectorySeparator(path2)); + bool needsSeparator = !(EndsInDirectorySeparator(path1) || PathInternal.StartsWithDirectorySeparator(path2)); int charsNeeded = path1.Length + path2.Length + (needsSeparator ? 1 : 0); if (destination.Length < charsNeeded) return false; @@ -569,8 +569,8 @@ namespace System.IO if (path3.Length == 0) return TryJoin(path1, path2, destination, out charsWritten); - int neededSeparators = PathInternal.EndsInDirectorySeparator(path1) || PathInternal.StartsWithDirectorySeparator(path2) ? 0 : 1; - bool needsSecondSeparator = !(PathInternal.EndsInDirectorySeparator(path2) || PathInternal.StartsWithDirectorySeparator(path3)); + int neededSeparators = EndsInDirectorySeparator(path1) || PathInternal.StartsWithDirectorySeparator(path2) ? 0 : 1; + bool needsSecondSeparator = !(EndsInDirectorySeparator(path2) || PathInternal.StartsWithDirectorySeparator(path3)); if (needsSecondSeparator) neededSeparators++; @@ -831,10 +831,10 @@ namespace System.IO // Trailing separators aren't significant for comparison int relativeToLength = relativeTo.Length; - if (PathInternal.EndsInDirectorySeparator(relativeTo.AsSpan())) + if (EndsInDirectorySeparator(relativeTo.AsSpan())) relativeToLength--; - bool pathEndsInSeparator = PathInternal.EndsInDirectorySeparator(path.AsSpan()); + bool pathEndsInSeparator = EndsInDirectorySeparator(path.AsSpan()); int pathLength = path.Length; if (pathEndsInSeparator) pathLength--; @@ -903,5 +903,33 @@ namespace System.IO StringComparison.OrdinalIgnoreCase; } } + + /// + /// Trims one trailing directory separator beyond the root of the path. + /// + public static string TrimEndingDirectorySeparator(string path) => + EndsInDirectorySeparator(path) && !PathInternal.IsRoot(path.AsSpan()) ? + path.Substring(0, path.Length - 1) : + path; + + /// + /// Trims one trailing directory separator beyond the root of the path. + /// + public static ReadOnlySpan TrimEndingDirectorySeparator(ReadOnlySpan path) => + EndsInDirectorySeparator(path) && !PathInternal.IsRoot(path) ? + path.Slice(0, path.Length - 1) : + path; + + /// + /// Returns true if the path ends in a directory separator. + /// + public static bool EndsInDirectorySeparator(ReadOnlySpan path) + => path.Length > 0 && PathInternal.IsDirectorySeparator(path[path.Length - 1]); + + /// + /// Returns true if the path ends in a directory separator. + /// + public static bool EndsInDirectorySeparator(string path) + => path != null && path.Length > 0 && PathInternal.IsDirectorySeparator(path[path.Length - 1]); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/PathInternal.cs b/src/libraries/System.Private.CoreLib/src/System/IO/PathInternal.cs index c6e1de4..3b864ac 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/PathInternal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/PathInternal.cs @@ -11,28 +11,12 @@ namespace System.IO internal static partial class PathInternal { /// - /// Returns true if the path ends in a directory separator. - /// - internal static bool EndsInDirectorySeparator(ReadOnlySpan path) - => path.Length > 0 && IsDirectorySeparator(path[path.Length - 1]); - - /// /// Returns true if the path starts in a directory separator. /// internal static bool StartsWithDirectorySeparator(ReadOnlySpan path) => path.Length > 0 && IsDirectorySeparator(path[0]); internal static string EnsureTrailingSeparator(string path) - => EndsInDirectorySeparator(path.AsSpan()) ? path : path + DirectorySeparatorCharAsString; - - internal static string TrimEndingDirectorySeparator(string path) => - EndsInDirectorySeparator(path.AsSpan()) && !IsRoot(path.AsSpan()) ? - path.Substring(0, path.Length - 1) : - path; - - internal static ReadOnlySpan TrimEndingDirectorySeparator(ReadOnlySpan path) => - EndsInDirectorySeparator(path) && !IsRoot(path) ? - path.Slice(0, path.Length - 1) : - path; + => Path.EndsInDirectorySeparator(path.AsSpan()) ? path : path + DirectorySeparatorCharAsString; internal static bool IsRoot(ReadOnlySpan path) => path.Length == GetRootLength(path); -- 2.7.4