namespace System.IO {
-#if NET_2_0
[ComVisible (true)]
public static class Path {
[Obsolete ("see GetInvalidPathChars and GetInvalidFileNameChars methods.")]
public static readonly char[] InvalidPathChars;
-#else
- public sealed class Path {
-
- private Path ()
- {
- }
-
- public static readonly char[] InvalidPathChars;
-#endif
public static readonly char AltDirectorySeparatorChar;
public static readonly char DirectorySeparatorChar;
public static readonly char PathSeparator;
internal static readonly string DirectorySeparatorStr;
public static readonly char VolumeSeparatorChar;
- private static readonly char[] PathSeparatorChars;
+ internal static readonly char[] PathSeparatorChars;
private static readonly bool dirEqualsVolume;
// class methods
return null;
if (path.IndexOfAny (InvalidPathChars) != -1)
- throw new ArgumentException ("Illegal characters in path", "path");
+ throw new ArgumentException ("Illegal characters in path.");
int iExt = findExtension (path);
return path1;
if (path1.IndexOfAny (InvalidPathChars) != -1)
- throw new ArgumentException ("Illegal characters in path", "path1");
+ throw new ArgumentException ("Illegal characters in path.");
if (path2.IndexOfAny (InvalidPathChars) != -1)
- throw new ArgumentException ("Illegal characters in path", "path2");
+ throw new ArgumentException ("Illegal characters in path.");
//TODO???: UNC names
- // LAMESPEC: MS says that if path1 is not empty and path2 is a full path
- // it should throw ArgumentException
if (IsPathRooted (path2))
return path2;
char p1end = path1 [path1.Length - 1];
if (p1end != DirectorySeparatorChar && p1end != AltDirectorySeparatorChar && p1end != VolumeSeparatorChar)
- return path1 + DirectorySeparatorChar.ToString () + path2;
+ return path1 + DirectorySeparatorStr + path2;
return path1 + path2;
}
if (path == null || GetPathRoot (path) == path)
return null;
- CheckArgument.WhitespaceOnly (path);
- CheckArgument.PathChars (path);
+ if (path.Trim ().Length == 0)
+ throw new ArgumentException ("Argument string consists of whitespace characters only.");
+
+ if (path.IndexOfAny (System.IO.Path.InvalidPathChars) > -1)
+ throw new ArgumentException ("Path contains invalid characters");
int nLast = path.LastIndexOfAny (PathSeparatorChars);
if (nLast == 0)
string ret = path.Substring (0, nLast);
int l = ret.Length;
- if (l >= 2 && ret [l - 1] == VolumeSeparatorChar)
+ if (l >= 2 && DirectorySeparatorChar == '\\' && ret [l - 1] == VolumeSeparatorChar)
return ret + DirectorySeparatorChar;
else {
//
return null;
if (path.IndexOfAny (InvalidPathChars) != -1)
- throw new ArgumentException ("Illegal characters in path", "path");
+ throw new ArgumentException ("Illegal characters in path.");
int iExt = findExtension (path);
return path;
if (path.IndexOfAny (InvalidPathChars) != -1)
- throw new ArgumentException ("Illegal characters in path", "path");
+ throw new ArgumentException ("Illegal characters in path.");
int nLast = path.LastIndexOfAny (PathSeparatorChars);
if (nLast >= 0)
public static string GetFullPath (string path)
{
string fullpath = InsecureGetFullPath (path);
+
+ SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
+
+#if !NET_2_1
if (SecurityManager.SecurityEnabled) {
new FileIOPermission (FileIOPermissionAccess.PathDiscovery, fullpath).Demand ();
}
+#endif
return fullpath;
}
// if the supplied path ends with a separator...
char end = path [path.Length - 1];
+ var canonicalize = true;
if (path.Length >= 2 &&
IsDsc (path [0]) &&
IsDsc (path [1])) {
if (path [0] != DirectorySeparatorChar)
path = path.Replace (AltDirectorySeparatorChar, DirectorySeparatorChar);
+
} else {
- if (!IsPathRooted (path))
+ if (!IsPathRooted (path)) {
+
+ // avoid calling expensive CanonicalizePath when possible
+ var start = 0;
+ while ((start = path.IndexOf ('.', start)) != -1) {
+ if (++start == path.Length || path [start] == DirectorySeparatorChar || path [start] == AltDirectorySeparatorChar)
+ break;
+ }
+ canonicalize = start > 0;
+
path = Directory.GetCurrentDirectory () + DirectorySeparatorStr + path;
- else if (DirectorySeparatorChar == '\\' &&
+ } else if (DirectorySeparatorChar == '\\' &&
path.Length >= 2 &&
IsDsc (path [0]) &&
!IsDsc (path [1])) { // like `\abc\def'
else
path = current.Substring (0, current.IndexOf ('\\', current.IndexOf ("\\\\") + 1));
}
- path = CanonicalizePath (path);
}
+
+ if (canonicalize)
+ path = CanonicalizePath (path);
// if the original ended with a [Alt]DirectorySeparatorChar then ensure the full path also ends with one
if (IsDsc (end) && (path [path.Length - 1] != DirectorySeparatorChar))
if (path == null)
return null;
- if (path.Length == 0)
- throw new ArgumentException ("This specified path is invalid.");
+ if (path.Trim ().Length == 0)
+ throw new ArgumentException ("The specified path is not of a legal form.");
if (!IsPathRooted (path))
return String.Empty;
Random rnd;
int num = 0;
+ SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
+
rnd = new Random ();
do {
num = rnd.Next ();
// avoid an endless loop
throw;
}
+ catch (UnauthorizedAccessException) {
+ // This can happen if we don't have write permission to /tmp
+ throw;
+ }
catch {
}
} while (f == null);
[EnvironmentPermission (SecurityAction.Demand, Unrestricted = true)]
public static string GetTempPath ()
{
+ SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
+
string p = get_temp_path ();
if (p.Length > 0 && p [p.Length - 1] != DirectorySeparatorChar)
return p + DirectorySeparatorChar;
private static extern string get_temp_path ();
public static bool HasExtension (string path)
- {
+ {
if (path == null || path.Trim ().Length == 0)
return false;
+ if (path.IndexOfAny (InvalidPathChars) != -1)
+ throw new ArgumentException ("Illegal characters in path.");
+
int pos = findExtension (path);
return 0 <= pos && pos < path.Length - 1;
}
(!dirEqualsVolume && path.Length > 1 && path [1] == VolumeSeparatorChar));
}
-#if NET_2_0
public static char[] GetInvalidFileNameChars ()
{
// return a new array as we do not want anyone to be able to change the values
return sb.ToString ();
}
-#endif
+
// private class methods
private static int findExtension (string path)
AltDirectorySeparatorChar = MonoIO.AltDirectorySeparatorChar;
PathSeparator = MonoIO.PathSeparator;
-#if NET_2_0
// this copy will be modifiable ("by design")
InvalidPathChars = GetInvalidPathChars ();
-#else
- if (Environment.IsRunningOnWindows) {
- InvalidPathChars = new char [15] { '\x00', '\x08', '\x10', '\x11', '\x12', '\x14', '\x15', '\x16',
- '\x17', '\x18', '\x19', '\x22', '\x3C', '\x3E', '\x7C' };
- } else {
- InvalidPathChars = new char [1] { '\x00' };
- }
-#endif
// internal fields
DirectorySeparatorStr = DirectorySeparatorChar.ToString ();
dirEqualsVolume = (DirectorySeparatorChar == VolumeSeparatorChar);
}
-
+
+ // returns the server and share part of a UNC. Assumes "path" is a UNC.
+ static string GetServerAndShare (string path)
+ {
+ int len = 2;
+ while (len < path.Length && !IsDsc (path [len])) len++;
+
+ if (len < path.Length) {
+ len++;
+ while (len < path.Length && !IsDsc (path [len])) len++;
+ }
+
+ return path.Substring (2, len - 2).Replace (AltDirectorySeparatorChar, DirectorySeparatorChar);
+ }
+
+ // assumes Environment.IsRunningOnWindows == true
static bool SameRoot (string root, string path)
{
// compare root - if enough details are available
if ((root.Length < 2) || (path.Length < 2))
return false;
+
+ // UNC handling
+ if (IsDsc (root[0]) && IsDsc (root[1])) {
+ if (!(IsDsc (path[0]) && IsDsc (path[1])))
+ return false;
+
+ string rootShare = GetServerAndShare (root);
+ string pathShare = GetServerAndShare (path);
+
+ return String.Compare (rootShare, pathShare, true, CultureInfo.InvariantCulture) == 0;
+ }
+
// same volume/drive
if (!root [0].Equals (path [0]))
return false;
- // presence if the separator
+ // presence of the separator
if (path[1] != Path.VolumeSeparatorChar)
return false;
if ((root.Length > 2) && (path.Length > 2)) {
// STEP 2: Check to see if this is only a root
string root = Path.GetPathRoot (path);
// it will return '\' for path '\', while it should return 'c:\' or so.
- // Note: commenting this out makes the ened for the (target == 1...) check in step 5
+ // Note: commenting this out makes the need for the (target == 1...) check in step 5
//if (root == path) return path;
// STEP 3: split the directories, this gets rid of consecutative "/"'s
// STEP 4: Get rid of directories containing . and ..
int target = 0;
+ bool isUnc = Environment.IsRunningOnWindows &&
+ root.Length > 2 && IsDsc (root[0]) && IsDsc (root[1]);
+
+ // Set an overwrite limit for UNC paths since '\' + server + share
+ // must not be eliminated by the '..' elimination algorithm.
+ int limit = isUnc ? 3 : 0;
+
for (int i = 0; i < dirs.Length; i++) {
+ // WIN32 path components must be trimmed
+ if (Environment.IsRunningOnWindows)
+ dirs[i] = dirs[i].TrimEnd ();
+
if (dirs[i] == "." || (i != 0 && dirs[i].Length == 0))
continue;
else if (dirs[i] == "..") {
- if (target != 0)
+ // don't overwrite path segments below the limit
+ if (target > limit)
target--;
} else
dirs[target++] = dirs[i];
else {
string ret = String.Join (DirectorySeparatorStr, dirs, 0, target);
if (Environment.IsRunningOnWindows) {
+ // append leading '\' of the UNC path that was lost in STEP 3.
+ if (isUnc)
+ ret = Path.DirectorySeparatorStr + ret;
+
if (!SameRoot (root, ret))
ret = root + ret;
- // In GetFullPath(), it is assured that here never comes UNC. So this must only applied to such path that starts with '\', without drive specification.
- if (!IsDsc (path[0]) && SameRoot (root, path)) {
+
+ if (isUnc) {
+ return ret;
+ } else if (!IsDsc (path[0]) && SameRoot (root, path)) {
if (ret.Length <= 2 && !ret.EndsWith (DirectorySeparatorStr)) // '\' after "c:"
ret += Path.DirectorySeparatorChar;
return ret;
return String.Compare (subset, slast, path, slast, subset.Length - slast) == 0;
}
+
+#if NET_4_0 || MOONLIGHT
+ public static string Combine (params string [] paths)
+ {
+ if (paths == null)
+ throw new ArgumentNullException ("paths");
+
+ int l = 0;
+ bool need_sep = false;
+ foreach (var s in paths){
+ if (s == null)
+ throw new ArgumentNullException ("One of the paths contains a null value", "paths");
+ if (s.IndexOfAny (InvalidPathChars) != -1)
+ throw new ArgumentException ("Illegal characters in path.");
+ if (l == 0 && s.Length > 0){
+ char p1end = s [s.Length - 1];
+ if (p1end != DirectorySeparatorChar && p1end != AltDirectorySeparatorChar && p1end != VolumeSeparatorChar){
+ need_sep = true;
+ l += DirectorySeparatorStr.Length;
+ }
+ }
+ }
+ var ret = new StringBuilder (l);
+ l = 0;
+ foreach (var s in paths){
+ if (IsPathRooted (s))
+ ret.Length = l = 0;
+ ret.Append (s);
+ if (l == 0 && need_sep)
+ ret.Append (DirectorySeparatorStr);
+ l = 1;
+ }
+
+ return ret.ToString ();
+ }
+
+ public static string Combine (string path1, string path2, string path3)
+ {
+ return Combine (new string [] { path1, path2, path3 });
+ }
+
+ public static string Combine (string path1, string path2, string path3, string path4)
+ {
+ return Combine (new string [] { path1, path2, path3, path4 });
+ }
+#endif
+
+ internal static void Validate (string path)
+ {
+ Validate (path, "path");
+ }
+
+ internal static void Validate (string path, string parameterName)
+ {
+ if (path == null)
+ throw new ArgumentNullException (parameterName);
+ if (String.IsNullOrWhiteSpace (path))
+ throw new ArgumentException (Locale.GetText ("Path is empty"));
+ if (path.IndexOfAny (Path.InvalidPathChars) != -1)
+ throw new ArgumentException (Locale.GetText ("Path contains invalid chars"));
+#if MOONLIGHT
+ // On Moonlight (SL4+) there are some limitations in "Elevated Trust"
+ if (SecurityManager.HasElevatedPermissions) {
+ }
+#endif
+ }
}
}