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.
5 using System
.Diagnostics
;
6 using System
.Runtime
.InteropServices
;
11 public static partial class Path
13 public static char[] GetInvalidFileNameChars() => new char[] { '\0', '/' }
;
15 public static char[] GetInvalidPathChars() => new char[] { '\0' }
;
17 // Expands the given path to a fully qualified path.
18 public static string GetFullPath(string path
)
21 throw new ArgumentNullException(nameof(path
));
24 throw new ArgumentException(SR
.Arg_PathEmpty
, nameof(path
));
26 if (path
.Contains('\0'))
27 throw new ArgumentException(SR
.Argument_InvalidPathChars
, nameof(path
));
29 // Expand with current directory if necessary
30 if (!IsPathRooted(path
))
32 path
= Combine(Interop
.Sys
.GetCwd(), path
);
35 // We would ideally use realpath to do this, but it resolves symlinks, requires that the file actually exist,
36 // and turns it into a full path, which we only want if fullCheck is true.
37 string collapsedString
= PathInternal
.RemoveRelativeSegments(path
, PathInternal
.GetRootLength(path
));
39 Debug
.Assert(collapsedString
.Length
< path
.Length
|| collapsedString
.ToString() == path
,
40 "Either we've removed characters, or the string should be unmodified from the input path.");
42 string result
= collapsedString
.Length
== 0 ? PathInternal
.DirectorySeparatorCharAsString
: collapsedString
;
47 public static string GetFullPath(string path
, string basePath
)
50 throw new ArgumentNullException(nameof(path
));
53 throw new ArgumentNullException(nameof(basePath
));
55 if (!IsPathFullyQualified(basePath
))
56 throw new ArgumentException(SR
.Arg_BasePathNotFullyQualified
, nameof(basePath
));
58 if (basePath
.Contains('\0') || path
.Contains('\0'))
59 throw new ArgumentException(SR
.Argument_InvalidPathChars
);
61 if (IsPathFullyQualified(path
))
62 return GetFullPath(path
);
64 return GetFullPath(CombineInternal(basePath
, path
));
67 private static string RemoveLongPathPrefix(string path
)
69 return path
; // nop. There's nothing special about "long" paths on Unix.
72 public static string GetTempPath()
74 const string TempEnvVar
= "TMPDIR";
75 const string DefaultTempPath
= "/tmp/";
77 // Get the temp path from the TMPDIR environment variable.
78 // If it's not set, just return the default path.
79 // If it is, return it, ensuring it ends with a slash.
80 string? path
= Environment
.GetEnvironmentVariable(TempEnvVar
);
82 string.IsNullOrEmpty(path
) ? DefaultTempPath
:
83 PathInternal
.IsDirectorySeparator(path
[path
.Length
- 1]) ? path
:
84 path
+ PathInternal
.DirectorySeparatorChar
;
87 public static string GetTempFileName()
89 const string Suffix
= ".tmp";
90 const int SuffixByteLength
= 4;
92 // mkstemps takes a char* and overwrites the XXXXXX with six characters
93 // that'll result in a unique file name.
94 string template
= GetTempPath() + "tmpXXXXXX" + Suffix
+ "\0";
95 byte[] name
= Encoding
.UTF8
.GetBytes(template
);
97 // Create, open, and close the temp file.
98 IntPtr fd
= Interop
.CheckIo(Interop
.Sys
.MksTemps(name
, SuffixByteLength
));
99 Interop
.Sys
.Close(fd
); // ignore any errors from close; nothing to do if cleanup isn't possible
101 // 'name' is now the name of the file
102 Debug
.Assert(name
[name
.Length
- 1] == '\0');
103 return Encoding
.UTF8
.GetString(name
, 0, name
.Length
- 1); // trim off the trailing '\0'
106 public static bool IsPathRooted(string? path
)
111 return IsPathRooted(path
.AsSpan());
114 public static bool IsPathRooted(ReadOnlySpan
<char> path
)
116 return path
.Length
> 0 && path
[0] == PathInternal
.DirectorySeparatorChar
;
120 /// Returns the path root or null if path is empty or null.
122 public static string? GetPathRoot(string? path
)
124 if (PathInternal
.IsEffectivelyEmpty(path
)) return null;
126 return IsPathRooted(path
) ? PathInternal
.DirectorySeparatorCharAsString
: string.Empty
;
129 public static ReadOnlySpan
<char> GetPathRoot(ReadOnlySpan
<char> path
)
131 return PathInternal
.IsEffectivelyEmpty(path
) && IsPathRooted(path
) ? PathInternal
.DirectorySeparatorCharAsString
.AsSpan() : ReadOnlySpan
<char>.Empty
;
134 /// <summary>Gets whether the system is case-sensitive.</summary>
135 internal static bool IsCaseSensitive