/// <summary> /// Returns offset as -1 if the path is not in Unc format, otherwise returns the root length. /// </summary> /// <param name="path"></param> /// <returns></returns> internal static int GetUncRootLength(ReadOnlySpan <char> path) { bool isDevice = PathInternal.IsDevice(path); if (!isDevice && StringSpanHelpers.Equals(path.Slice(0, 2), @"\\")) { return(2); } else if (isDevice && path.Length >= 8 && (StringSpanHelpers.Equals(path.Slice(0, 8), PathInternal.UncExtendedPathPrefix) || StringSpanHelpers.Equals(path.Slice(5, 4), @"UNC\"))) { return(8); } return(-1); }
public static string GetFullPath(string path, string basePath) { if (path == null) { throw new ArgumentNullException(nameof(path)); } if (basePath == null) { throw new ArgumentNullException(nameof(basePath)); } if (!IsPathFullyQualified(basePath)) { throw new ArgumentException(SR.Arg_BasePathNotFullyQualified, nameof(basePath)); } if (basePath.Contains('\0') || path.Contains('\0')) { throw new ArgumentException(SR.Argument_InvalidPathChars); } if (IsPathFullyQualified(path)) { return(GetFullPath(path)); } int length = path.Length; string combinedPath = null; if ((length >= 1 && PathInternal.IsDirectorySeparator(path[0]))) { // Path is current drive rooted i.e. starts with \: // "\Foo" and "C:\Bar" => "C:\Foo" // "\Foo" and "\\?\C:\Bar" => "\\?\C:\Foo" combinedPath = Join(GetPathRoot(basePath.AsSpan()), path); } else if (length >= 2 && PathInternal.IsValidDriveChar(path[0]) && path[1] == PathInternal.VolumeSeparatorChar) { // Drive relative paths Debug.Assert(length == 2 || !PathInternal.IsDirectorySeparator(path[2])); if (StringSpanHelpers.Equals(GetVolumeName(path), GetVolumeName(basePath))) { // Matching root // "C:Foo" and "C:\Bar" => "C:\Bar\Foo" // "C:Foo" and "\\?\C:\Bar" => "\\?\C:\Bar\Foo" combinedPath = Join(basePath, path.AsSpan().Slice(2)); } else { // No matching root, root to specified drive // "D:Foo" and "C:\Bar" => "D:Foo" // "D:Foo" and "\\?\C:\Bar" => "\\?\D:\Foo" combinedPath = !PathInternal.IsDevice(basePath) ? path.Insert(2, @"\") : length == 2 ? JoinInternal(basePath.AsSpan().Slice(0, 4), path, @"\") : JoinInternal(basePath.AsSpan().Slice(0, 4), path.AsSpan().Slice(0, 2), @"\", path.AsSpan().Slice(2)); } } else { // "Simple" relative path // "Foo" and "C:\Bar" => "C:\Bar\Foo" // "Foo" and "\\?\C:\Bar" => "\\?\C:\Bar\Foo" combinedPath = JoinInternal(basePath, path); } // Device paths are normalized by definition, so passing something of this format // to GetFullPath() won't do anything by design. Additionally, GetFullPathName() in // Windows doesn't root them properly. As such we need to manually remove segments. return(PathInternal.IsDevice(combinedPath) ? RemoveRelativeSegments(combinedPath, PathInternal.GetRootLength(combinedPath)) : GetFullPath(combinedPath)); }