Path.Unix.cs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  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. using System.Diagnostics;
  5. using System.Runtime.InteropServices;
  6. using System.Text;
  7. namespace System.IO
  8. {
  9. public static partial class Path
  10. {
  11. public static char[] GetInvalidFileNameChars() => new char[] { '\0', '/' };
  12. public static char[] GetInvalidPathChars() => new char[] { '\0' };
  13. // Expands the given path to a fully qualified path.
  14. public static string GetFullPath(string path)
  15. {
  16. if (path == null)
  17. throw new ArgumentNullException(nameof(path));
  18. if (path.Length == 0)
  19. throw new ArgumentException(SR.Arg_PathEmpty, nameof(path));
  20. if (path.Contains('\0'))
  21. throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path));
  22. // Expand with current directory if necessary
  23. if (!IsPathRooted(path))
  24. {
  25. path = Combine(Interop.Sys.GetCwd(), path);
  26. }
  27. // We would ideally use realpath to do this, but it resolves symlinks, requires that the file actually exist,
  28. // and turns it into a full path, which we only want if fullCheck is true.
  29. string collapsedString = PathInternal.RemoveRelativeSegments(path, PathInternal.GetRootLength(path));
  30. Debug.Assert(collapsedString.Length < path.Length || collapsedString.ToString() == path,
  31. "Either we've removed characters, or the string should be unmodified from the input path.");
  32. string result = collapsedString.Length == 0 ? PathInternal.DirectorySeparatorCharAsString : collapsedString;
  33. return result;
  34. }
  35. public static string GetFullPath(string path, string basePath)
  36. {
  37. if (path == null)
  38. throw new ArgumentNullException(nameof(path));
  39. if (basePath == null)
  40. throw new ArgumentNullException(nameof(basePath));
  41. if (!IsPathFullyQualified(basePath))
  42. throw new ArgumentException(SR.Arg_BasePathNotFullyQualified, nameof(basePath));
  43. if (basePath.Contains('\0') || path.Contains('\0'))
  44. throw new ArgumentException(SR.Argument_InvalidPathChars);
  45. if (IsPathFullyQualified(path))
  46. return GetFullPath(path);
  47. return GetFullPath(CombineInternal(basePath, path));
  48. }
  49. private static string RemoveLongPathPrefix(string path)
  50. {
  51. return path; // nop. There's nothing special about "long" paths on Unix.
  52. }
  53. public static string GetTempPath()
  54. {
  55. const string TempEnvVar = "TMPDIR";
  56. const string DefaultTempPath = "/tmp/";
  57. // Get the temp path from the TMPDIR environment variable.
  58. // If it's not set, just return the default path.
  59. // If it is, return it, ensuring it ends with a slash.
  60. string path = Environment.GetEnvironmentVariable(TempEnvVar);
  61. return
  62. string.IsNullOrEmpty(path) ? DefaultTempPath :
  63. PathInternal.IsDirectorySeparator(path[path.Length - 1]) ? path :
  64. path + PathInternal.DirectorySeparatorChar;
  65. }
  66. public static string GetTempFileName()
  67. {
  68. const string Suffix = ".tmp";
  69. const int SuffixByteLength = 4;
  70. // mkstemps takes a char* and overwrites the XXXXXX with six characters
  71. // that'll result in a unique file name.
  72. string template = GetTempPath() + "tmpXXXXXX" + Suffix + "\0";
  73. byte[] name = Encoding.UTF8.GetBytes(template);
  74. // Create, open, and close the temp file.
  75. IntPtr fd = Interop.CheckIo(Interop.Sys.MksTemps(name, SuffixByteLength));
  76. Interop.Sys.Close(fd); // ignore any errors from close; nothing to do if cleanup isn't possible
  77. // 'name' is now the name of the file
  78. Debug.Assert(name[name.Length - 1] == '\0');
  79. return Encoding.UTF8.GetString(name, 0, name.Length - 1); // trim off the trailing '\0'
  80. }
  81. public static bool IsPathRooted(string path)
  82. {
  83. if (path == null)
  84. return false;
  85. return IsPathRooted(path.AsSpan());
  86. }
  87. public static bool IsPathRooted(ReadOnlySpan<char> path)
  88. {
  89. return path.Length > 0 && path[0] == PathInternal.DirectorySeparatorChar;
  90. }
  91. /// <summary>
  92. /// Returns the path root or null if path is empty or null.
  93. /// </summary>
  94. public static string GetPathRoot(string path)
  95. {
  96. if (PathInternal.IsEffectivelyEmpty(path)) return null;
  97. return IsPathRooted(path) ? PathInternal.DirectorySeparatorCharAsString : string.Empty;
  98. }
  99. public static ReadOnlySpan<char> GetPathRoot(ReadOnlySpan<char> path)
  100. {
  101. return PathInternal.IsEffectivelyEmpty(path) && IsPathRooted(path) ? PathInternal.DirectorySeparatorCharAsString.AsSpan() : ReadOnlySpan<char>.Empty;
  102. }
  103. /// <summary>Gets whether the system is case-sensitive.</summary>
  104. internal static bool IsCaseSensitive
  105. {
  106. get
  107. {
  108. #if PLATFORM_OSX
  109. return false;
  110. #else
  111. return true;
  112. #endif
  113. }
  114. }
  115. }
  116. }