static string CanonicalizePath (string path)
{
// STEP 1: Check for empty string
if (path == null)
return path;
if (Environment.IsRunningOnWindows)
path = path.Trim ();
if (path.Length == 0)
return path;
// 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 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
string[] dirs = path.Split (Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
// 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] == "..") {
// don't overwrite path segments below the limit
if (target > limit)
target--;
} else
dirs[target++] = dirs[i];
}
// STEP 5: Combine everything.
if (target == 0 || (target == 1 && dirs[0] == ""))
return root;
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;
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;
} else {
string current = Directory.GetCurrentDirectory ();
if (current.Length > 1 && current[1] == Path.VolumeSeparatorChar) {
// DOS local file path
if (ret.Length == 0 || IsDsc (ret[0]))
ret += '\\';
return current.Substring (0, 2) + ret;
} else if (IsDsc (current[current.Length - 1]) && IsDsc (ret[0]))
return current + ret.Substring (1);
else
return current + ret;
}
}
return ret;
}
}