Ejemplo n.º 1
0
        public DirectoryInfo CreateSubdirectory(string path)
        {
            if (path == null)
            {
                throw new ArgumentNullException(nameof(path));
            }
            if (PathInternal.IsEffectivelyEmpty(path.AsSpan()))
            {
                throw new ArgumentException(SR.Argument_PathEmpty, nameof(path));
            }
            if (Path.IsPathRooted(path))
            {
                throw new ArgumentException(SR.Arg_Path2IsRooted, nameof(path));
            }

            string newPath = Path.GetFullPath(Path.Combine(FullPath, path));

            ReadOnlySpan <char> trimmedNewPath     = Path.TrimEndingDirectorySeparator(newPath.AsSpan());
            ReadOnlySpan <char> trimmedCurrentPath = Path.TrimEndingDirectorySeparator(FullPath.AsSpan());

            // We want to make sure the requested directory is actually under the subdirectory.
            if (trimmedNewPath.StartsWith(trimmedCurrentPath, PathInternal.StringComparison)
                // Allow the exact same path, but prevent allowing "..\FooBar" through when the directory is "Foo"
                && ((trimmedNewPath.Length == trimmedCurrentPath.Length) || PathInternal.IsDirectorySeparator(newPath[trimmedCurrentPath.Length])))
            {
                FileSystem.CreateDirectory(newPath);
                return(new DirectoryInfo(newPath));
            }

            // We weren't nested
            throw new ArgumentException(SR.Format(SR.Argument_InvalidSubPath, path, FullPath), nameof(path));
        }
Ejemplo n.º 2
0
        public static string GetFullPath(string path)
        {
            if (path == null)
            {
                throw new ArgumentNullException(nameof(path));
            }

            // If the path would normalize to string empty, we'll consider it empty
            if (PathInternal.IsEffectivelyEmpty(path.AsSpan()))
            {
                throw new ArgumentException(SR.Arg_PathEmpty, nameof(path));
            }

            // Embedded null characters are the only invalid character case we trully care about.
            // This is because the nulls will signal the end of the string to Win32 and therefore have
            // unpredictable results.
            if (path.Contains('\0'))
            {
                throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path));
            }

            if (PathInternal.IsExtended(path.AsSpan()))
            {
                // \\?\ paths are considered normalized by definition. Windows doesn't normalize \\?\
                // paths and neither should we. Even if we wanted to GetFullPathName does not work
                // properly with device paths. If one wants to pass a \\?\ path through normalization
                // one can chop off the prefix, pass it to GetFullPath and add it again.
                return(path);
            }

            return(PathHelper.Normalize(path));
        }
Ejemplo n.º 3
0
        public static ReadOnlySpan <char> GetPathRoot(ReadOnlySpan <char> path)
        {
            if (PathInternal.IsEffectivelyEmpty(path))
            {
                return(ReadOnlySpan <char> .Empty);
            }

            int pathRoot = PathInternal.GetRootLength(path);

            return(pathRoot <= 0 ? ReadOnlySpan <char> .Empty : path.Slice(0, pathRoot));
        }
Ejemplo n.º 4
0
        public static ReadOnlySpan <char> GetDirectoryName(ReadOnlySpan <char> path)
        {
            if (PathInternal.IsEffectivelyEmpty(path))
            {
                return(ReadOnlySpan <char> .Empty);
            }

            int end = GetDirectoryNameOffset(path);

            return(end >= 0 ? path.Slice(0, end) : ReadOnlySpan <char> .Empty);
        }
Ejemplo n.º 5
0
        public static string?GetDirectoryName(string?path)
        {
            if (path == null || PathInternal.IsEffectivelyEmpty(path.AsSpan()))
            {
                return(null);
            }

            int end = GetDirectoryNameOffset(path.AsSpan());

            return(end >= 0 ? PathInternal.NormalizeDirectorySeparators(path.Substring(0, end)) : null);
        }
Ejemplo n.º 6
0
        public static string?GetDirectoryName(string?path)   // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/26761
        {
            if (path == null || PathInternal.IsEffectivelyEmpty(path.AsSpan()))
            {
                return(null);
            }

            int end = GetDirectoryNameOffset(path.AsSpan());

            return(end >= 0 ? PathInternal.NormalizeDirectorySeparators(path.Substring(0, end)) : null);
        }
Ejemplo n.º 7
0
        public static string?GetPathRoot(string?path)
        {
            if (PathInternal.IsEffectivelyEmpty(path.AsSpan()))
            {
                return(null);
            }

            ReadOnlySpan <char> result = GetPathRoot(path.AsSpan());

            if (path !.Length == result.Length)
            {
                return(PathInternal.NormalizeDirectorySeparators(path));
            }

            return(PathInternal.NormalizeDirectorySeparators(result.ToString()));
        }
Ejemplo n.º 8
0
        // Expands the given path to a fully qualified path.
        public static string GetFullPath(string path)
        {
            if (path == null)
            {
                throw new ArgumentNullException(nameof(path));
            }

            // If the path would normalize to string empty, we'll consider it empty
            if (PathInternal.IsEffectivelyEmpty(path.AsSpan()))
            {
                throw new ArgumentException(SR.Arg_PathEmpty, nameof(path));
            }

            // Embedded null characters are the only invalid character case we trully care about.
            // This is because the nulls will signal the end of the string to Win32 and therefore have
            // unpredictable results.
            if (path.Contains('\0'))
            {
                throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path));
            }

            return(GetFullyQualifiedPath(path));
        }
Ejemplo n.º 9
0
        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));
            }

            if (PathInternal.IsEffectivelyEmpty(path.AsSpan()))
            {
                return(basePath);
            }

            int    length = path.Length;
            string combinedPath;

            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.AsSpan(1)); // Cut the separator to ensure we don't end up with two separators when joining with the root.
            }
            else if (length >= 2 && PathInternal.IsValidDriveChar(path[0]) && path[1] == PathInternal.VolumeSeparatorChar)
            {
                // Drive relative paths
                Debug.Assert(length == 2 || !PathInternal.IsDirectorySeparator(path[2]));

                if (GetVolumeName(path.AsSpan()).EqualsOrdinal(GetVolumeName(basePath.AsSpan())))
                {
                    // Matching root
                    // "C:Foo" and "C:\Bar" => "C:\Bar\Foo"
                    // "C:Foo" and "\\?\C:\Bar" => "\\?\C:\Bar\Foo"
                    combinedPath = Join(basePath.AsSpan(), path.AsSpan(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.AsSpan())
                        ? path.Insert(2, @"\")
                        : length == 2
                            ? JoinInternal(basePath.AsSpan(0, 4), path.AsSpan(), @"\".AsSpan())
                            : JoinInternal(basePath.AsSpan(0, 4), path.AsSpan(0, 2), @"\".AsSpan(), path.AsSpan(2));
                }
            }
            else
            {
                // "Simple" relative path
                // "Foo" and "C:\Bar" => "C:\Bar\Foo"
                // "Foo" and "\\?\C:\Bar" => "\\?\C:\Bar\Foo"
                combinedPath = JoinInternal(basePath.AsSpan(), path.AsSpan());
            }

            // Device paths are normalized by definition, so passing something of this format (i.e. \\?\C:\.\tmp, \\.\C:\foo)
            // to Windows APIs won't do anything by design. Additionally, GetFullPathName() in Windows doesn't root
            // them properly. As such we need to manually remove segments and not use GetFullPath().

            return(PathInternal.IsDevice(combinedPath.AsSpan())
                ? PathInternal.RemoveRelativeSegments(combinedPath, PathInternal.GetRootLength(combinedPath.AsSpan()))
                : GetFullPath(combinedPath));
        }
Ejemplo n.º 10
0
    private static string GetRelativePath(string relativeTo, string path, StringComparison comparisonType)
    {
        if (relativeTo == null)
        {
            throw new ArgumentNullException(nameof(relativeTo));
        }
        if (PathInternal.IsEffectivelyEmpty(relativeTo.AsSpan()))
        {
            throw new ArgumentException(SR.Arg_PathEmpty, nameof(relativeTo));
        }
        if (path == null)
        {
            throw new ArgumentNullException(nameof(path));
        }
        if (PathInternal.IsEffectivelyEmpty(path.AsSpan()))
        {
            throw new ArgumentException(SR.Arg_PathEmpty, nameof(path));
        }
        Debug.Assert(comparisonType == StringComparison.Ordinal || comparisonType == StringComparison.OrdinalIgnoreCase);
        relativeTo = GetFullPath(relativeTo);
        path       = GetFullPath(path);
        // Need to check if the roots are different- if they are we need to return the "to" path.
        if (!PathInternal.AreRootsEqual(relativeTo, path, comparisonType))
        {
            return(path);
        }
        int commonLength = PathInternal.GetCommonPathLength(relativeTo, path, ignoreCase: comparisonType == StringComparison.OrdinalIgnoreCase);

        // If there is nothing in common they can't share the same root, return the "to" path as is.
        if (commonLength == 0)
        {
            return(path);
        }
        // Trailing separators aren't significant for comparison
        int relativeToLength = relativeTo.Length;

        if (EndsInDirectorySeparator(relativeTo.AsSpan()))
        {
            relativeToLength--;
        }
        bool pathEndsInSeparator = EndsInDirectorySeparator(path.AsSpan());
        int  pathLength          = path.Length;

        if (pathEndsInSeparator)
        {
            pathLength--;
        }
        // If we have effectively the same path, return "."
        if (relativeToLength == pathLength && commonLength >= relativeToLength)
        {
            return(".");
        }
        // We have the same root, we need to calculate the difference now using the
        // common Length and Segment count past the length.
        //
        // Some examples:
        //
        //  C:\Foo C:\Bar L3, S1 -> ..\Bar
        //  C:\Foo C:\Foo\Bar L6, S0 -> Bar
        //  C:\Foo\Bar C:\Bar\Bar L3, S2 -> ..\..\Bar\Bar
        //  C:\Foo\Foo C:\Foo\Bar L7, S1 -> ..\Bar
        // Original: var sb = new ValueStringBuilder(stackalloc char[260]);
        var sb = new StringBuilder(260);

        sb.EnsureCapacity(Math.Max(relativeTo.Length, path.Length));
        // Add parent segments for segments past the common on the "from" path
        if (commonLength < relativeToLength)
        {
            sb.Append("..");
            for (int i = commonLength + 1; i < relativeToLength; i++)
            {
                if (PathInternal.IsDirectorySeparator(relativeTo[i]))
                {
                    sb.Append(DirectorySeparatorChar);
                    sb.Append("..");
                }
            }
        }
        else if (PathInternal.IsDirectorySeparator(path[commonLength]))
        {
            // No parent segments and we need to eat the initial separator
            //  (C:\Foo C:\Foo\Bar case)
            commonLength++;
        }
        // Now add the rest of the "to" path, adding back the trailing separator
        int differenceLength = pathLength - commonLength;

        if (pathEndsInSeparator)
        {
            differenceLength++;
        }
        if (differenceLength > 0)
        {
            if (sb.Length > 0)
            {
                sb.Append(DirectorySeparatorChar);
            }
            sb.Append(path.AsSpan(commonLength, differenceLength));
        }
        return(sb.ToString());
    }