PathInternal.Unix.cs 3.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. #nullable enable
  5. using System.Diagnostics;
  6. using System.Text;
  7. using System.Runtime.InteropServices;
  8. namespace System.IO
  9. {
  10. /// <summary>Contains internal path helpers that are shared between many projects.</summary>
  11. internal static partial class PathInternal
  12. {
  13. internal const char DirectorySeparatorChar = '/';
  14. internal const char AltDirectorySeparatorChar = '/';
  15. internal const char VolumeSeparatorChar = '/';
  16. internal const char PathSeparator = ':';
  17. internal const string DirectorySeparatorCharAsString = "/";
  18. internal const string ParentDirectoryPrefix = @"../";
  19. internal static int GetRootLength(ReadOnlySpan<char> path)
  20. {
  21. return path.Length > 0 && IsDirectorySeparator(path[0]) ? 1 : 0;
  22. }
  23. internal static bool IsDirectorySeparator(char c)
  24. {
  25. // The alternate directory separator char is the same as the directory separator,
  26. // so we only need to check one.
  27. Debug.Assert(DirectorySeparatorChar == AltDirectorySeparatorChar);
  28. return c == DirectorySeparatorChar;
  29. }
  30. /// <summary>
  31. /// Normalize separators in the given path. Compresses forward slash runs.
  32. /// </summary>
  33. internal static string NormalizeDirectorySeparators(string path)
  34. {
  35. if (string.IsNullOrEmpty(path))
  36. return path;
  37. // Make a pass to see if we need to normalize so we can potentially skip allocating
  38. bool normalized = true;
  39. for (int i = 0; i < path.Length; i++)
  40. {
  41. if (IsDirectorySeparator(path[i])
  42. && (i + 1 < path.Length && IsDirectorySeparator(path[i + 1])))
  43. {
  44. normalized = false;
  45. break;
  46. }
  47. }
  48. if (normalized)
  49. return path;
  50. StringBuilder builder = new StringBuilder(path.Length);
  51. for (int i = 0; i < path.Length; i++)
  52. {
  53. char current = path[i];
  54. // Skip if we have another separator following
  55. if (IsDirectorySeparator(current)
  56. && (i + 1 < path.Length && IsDirectorySeparator(path[i + 1])))
  57. continue;
  58. builder.Append(current);
  59. }
  60. return builder.ToString();
  61. }
  62. internal static bool IsPartiallyQualified(ReadOnlySpan<char> path)
  63. {
  64. // This is much simpler than Windows where paths can be rooted, but not fully qualified (such as Drive Relative)
  65. // As long as the path is rooted in Unix it doesn't use the current directory and therefore is fully qualified.
  66. return !Path.IsPathRooted(path);
  67. }
  68. /// <summary>
  69. /// Returns true if the path is effectively empty for the current OS.
  70. /// For unix, this is empty or null. For Windows, this is empty, null, or
  71. /// just spaces ((char)32).
  72. /// </summary>
  73. internal static bool IsEffectivelyEmpty(string? path)
  74. {
  75. return string.IsNullOrEmpty(path);
  76. }
  77. internal static bool IsEffectivelyEmpty(ReadOnlySpan<char> path)
  78. {
  79. return path.IsEmpty;
  80. }
  81. }
  82. }