diff options
Diffstat (limited to 'src/SMAPI.Toolkit/Utilities/PathUtilities.cs')
-rw-r--r-- | src/SMAPI.Toolkit/Utilities/PathUtilities.cs | 41 |
1 files changed, 34 insertions, 7 deletions
diff --git a/src/SMAPI.Toolkit/Utilities/PathUtilities.cs b/src/SMAPI.Toolkit/Utilities/PathUtilities.cs index e9d71747..bd5fafbc 100644 --- a/src/SMAPI.Toolkit/Utilities/PathUtilities.cs +++ b/src/SMAPI.Toolkit/Utilities/PathUtilities.cs @@ -10,6 +10,13 @@ namespace StardewModdingAPI.Toolkit.Utilities public static class PathUtilities { /********* + ** Fields + *********/ + /// <summary>The root prefix for a Windows UNC path.</summary> + private const string WindowsUncRoot = @"\\"; + + + /********* ** Accessors *********/ /// <summary>The possible directory separator characters in a file path.</summary> @@ -25,6 +32,7 @@ namespace StardewModdingAPI.Toolkit.Utilities /// <summary>Get the segments from a path (e.g. <c>/usr/bin/example</c> => <c>usr</c>, <c>bin</c>, and <c>example</c>).</summary> /// <param name="path">The path to split.</param> /// <param name="limit">The number of segments to match. Any additional segments will be merged into the last returned part.</param> + [Pure] public static string[] GetSegments(string path, int? limit = null) { return limit.HasValue @@ -37,16 +45,20 @@ namespace StardewModdingAPI.Toolkit.Utilities [Pure] public static string NormalizePathSeparators(string path) { - string[] parts = PathUtilities.GetSegments(path); - string normalized = string.Join(PathUtilities.PreferredPathSeparator, parts); - if (path.StartsWith(PathUtilities.PreferredPathSeparator)) - normalized = PathUtilities.PreferredPathSeparator + normalized; // keep root slash - return normalized; + if (string.IsNullOrWhiteSpace(path)) + return path; + + return string.Join(PathUtilities.PreferredPathSeparator, path.Split(PathUtilities.PossiblePathSeparators)); } - /// <summary>Get a directory or file path relative to a given source path.</summary> + /// <summary>Get a directory or file path relative to a given source path. If no relative path is possible (e.g. the paths are on different drives), an absolute path is returned.</summary> /// <param name="sourceDir">The source folder path.</param> /// <param name="targetPath">The target folder or file path.</param> + /// <remarks> + /// + /// NOTE: this is a heuristic implementation that works in the cases SMAPI needs it for, but it doesn't handle all edge cases (e.g. case-sensitivity on Linux, or traversing between UNC paths on Windows). This should be replaced with the more comprehensive <c>Path.GetRelativePath</c> if the game ever migrates to .NET Core. + /// + /// </remarks> [Pure] public static string GetRelativePath(string sourceDir, string targetPath) { @@ -58,13 +70,27 @@ namespace StardewModdingAPI.Toolkit.Utilities // get relative path string relative = PathUtilities.NormalizePathSeparators(Uri.UnescapeDataString(from.MakeRelativeUri(to).ToString())); + + // normalize if (relative == "") - relative = "./"; + relative = "."; + else + { + // trim trailing slash from URL + if (relative.EndsWith(PathUtilities.PreferredPathSeparator)) + relative = relative.Substring(0, relative.Length - PathUtilities.PreferredPathSeparator.Length); + + // fix root + if (relative.StartsWith("file:") && !targetPath.Contains("file:")) + relative = relative.Substring("file:".Length); + } + return relative; } /// <summary>Get whether a path is relative and doesn't try to climb out of its containing folder (e.g. doesn't contain <c>../</c>).</summary> /// <param name="path">The path to check.</param> + [Pure] public static bool IsSafeRelativePath(string path) { if (string.IsNullOrWhiteSpace(path)) @@ -77,6 +103,7 @@ namespace StardewModdingAPI.Toolkit.Utilities /// <summary>Get whether a string is a valid 'slug', containing only basic characters that are safe in all contexts (e.g. filenames, URLs, etc).</summary> /// <param name="str">The string to check.</param> + [Pure] public static bool IsSlug(string str) { return !Regex.IsMatch(str, "[^a-z0-9_.-]", RegexOptions.IgnoreCase); |