Пример #1
0
        /// <summary>
        /// Try to remove relative segments from the given path (without combining with a root).
        /// </summary>
        /// <param name="skip">Skip the specified number of characters before evaluating.</param>
        private static string RemoveRelativeSegments(string path, int skip = 0)
        {
            bool flippedSeparator = false;

            // Remove "//", "/./", and "/../" from the path by copying each character to the output,
            // except the ones we're removing, such that the builder contains the normalized path
            // at the end.
            var sb = StringBuilderCache.Acquire(path.Length);

            if (skip > 0)
            {
                sb.Append(path, 0, skip);
            }

            int componentCharCount = 0;

            for (int i = skip; i < path.Length; i++)
            {
                char c = path[i];

                if (PathInternal.IsDirectorySeparator(c) && i + 1 < path.Length)
                {
                    componentCharCount = 0;

                    // Skip this character if it's a directory separator and if the next character is, too,
                    // e.g. "parent//child" => "parent/child"
                    if (PathInternal.IsDirectorySeparator(path[i + 1]))
                    {
                        continue;
                    }

                    // Skip this character and the next if it's referring to the current directory,
                    // e.g. "parent/./child" =? "parent/child"
                    if ((i + 2 == path.Length || PathInternal.IsDirectorySeparator(path[i + 2])) &&
                        path[i + 1] == '.')
                    {
                        i++;
                        continue;
                    }

                    // Skip this character and the next two if it's referring to the parent directory,
                    // e.g. "parent/child/../grandchild" => "parent/grandchild"
                    if (i + 2 < path.Length &&
                        (i + 3 == path.Length || PathInternal.IsDirectorySeparator(path[i + 3])) &&
                        path[i + 1] == '.' && path[i + 2] == '.')
                    {
                        // Unwind back to the last slash (and if there isn't one, clear out everything).
                        int s;
                        for (s = sb.Length - 1; s >= 0; s--)
                        {
                            if (PathInternal.IsDirectorySeparator(sb[s]))
                            {
                                sb.Length = s;
                                break;
                            }
                        }
                        if (s < 0)
                        {
                            sb.Length = 0;
                        }

                        i += 2;
                        continue;
                    }
                }

                if (++componentCharCount > PathInternal.MaxComponentLength)
                {
                    throw new PathTooLongException(SR.IO_PathTooLong);
                }

                // Normalize the directory separator if needed
                if (c != Path.DirectorySeparatorChar && c == Path.AltDirectorySeparatorChar)
                {
                    c = Path.DirectorySeparatorChar;
                    flippedSeparator = true;
                }

                sb.Append(c);
            }

            if (flippedSeparator || sb.Length != path.Length)
            {
                return(StringBuilderCache.GetStringAndRelease(sb));
            }
            else
            {
                // We haven't changed the source path, return the original
                StringBuilderCache.Release(sb);
                return(path);
            }
        }
Пример #2
0
        public virtual String ReadString()
        {
            Contract.Ensures(Contract.Result <String>() != null);

            if (m_stream == null)
            {
                __Error.FileNotOpen();
            }

            int currPos = 0;
            int n;
            int stringLength;
            int readLength;
            int charsRead;

            // Length of the string in bytes, not chars
            stringLength = Read7BitEncodedInt();
            if (stringLength < 0)
            {
                throw new IOException(Environment.GetResourceString("IO.IO_InvalidStringLen_Len", stringLength));
            }

            if (stringLength == 0)
            {
                return(String.Empty);
            }

            if (m_charBytes == null)
            {
                m_charBytes = new byte[MaxCharBytesSize];
            }

            if (m_charBuffer == null)
            {
                m_charBuffer = new char[m_maxCharsSize];
            }

            StringBuilder sb = null;

            do
            {
                readLength = ((stringLength - currPos) > MaxCharBytesSize)?MaxCharBytesSize:(stringLength - currPos);

                n = m_stream.Read(m_charBytes, 0, readLength);
                if (n == 0)
                {
                    __Error.EndOfFile();
                }

                charsRead = m_decoder.GetChars(m_charBytes, 0, n, m_charBuffer, 0);

                if (currPos == 0 && n == stringLength)
                {
                    return(new String(m_charBuffer, 0, charsRead));
                }

                if (sb == null)
                {
                    sb = StringBuilderCache.Acquire(stringLength); // Actual string length in chars may be smaller.
                }
                sb.Append(m_charBuffer, 0, charsRead);
                currPos += n;
            } while (currPos < stringLength);

            return(StringBuilderCache.GetStringAndRelease(sb));
        }
Пример #3
0
        public static string Combine(params string[] paths)
        {
            if (paths == null)
            {
                throw new ArgumentNullException(nameof(paths));
            }

            int finalSize      = 0;
            int firstComponent = 0;

            // We have two passes, the first calculates how large a buffer to allocate and does some precondition
            // checks on the paths passed in.  The second actually does the combination.

            for (int i = 0; i < paths.Length; i++)
            {
                if (paths[i] == null)
                {
                    throw new ArgumentNullException(nameof(paths));
                }

                if (paths[i].Length == 0)
                {
                    continue;
                }

                if (IsPathRooted(paths[i]))
                {
                    firstComponent = i;
                    finalSize      = paths[i].Length;
                }
                else
                {
                    finalSize += paths[i].Length;
                }

                char ch = paths[i][paths[i].Length - 1];
                if (!PathInternal.IsDirectorySeparator(ch))
                {
                    finalSize++;
                }
            }

            StringBuilder finalPath = StringBuilderCache.Acquire(finalSize);

            for (int i = firstComponent; i < paths.Length; i++)
            {
                if (paths[i].Length == 0)
                {
                    continue;
                }

                if (finalPath.Length == 0)
                {
                    finalPath.Append(paths[i]);
                }
                else
                {
                    char ch = finalPath[finalPath.Length - 1];
                    if (!PathInternal.IsDirectorySeparator(ch))
                    {
                        finalPath.Append(PathInternal.DirectorySeparatorChar);
                    }

                    finalPath.Append(paths[i]);
                }
            }

            return(StringBuilderCache.GetStringAndRelease(finalPath));
        }
Пример #4
0
        private static string GetRelativePath(string relativeTo, string path, StringComparison comparisonType)
        {
            if (string.IsNullOrEmpty(relativeTo))
            {
                throw new ArgumentNullException(nameof(relativeTo));
            }
            if (PathInternal.IsEffectivelyEmpty(path))
            {
                throw new ArgumentNullException(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 (PathInternal.EndsInDirectorySeparator(relativeTo))
            {
                relativeToLength--;
            }

            bool pathEndsInSeparator = PathInternal.EndsInDirectorySeparator(path);
            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

            StringBuilder sb = StringBuilderCache.Acquire(Math.Max(relativeTo.Length, path.Length));

            // Add parent segments for segments past the common on the "from" path
            if (commonLength < relativeToLength)
            {
                sb.Append(PathInternal.ParentDirectoryPrefix);

                for (int i = commonLength; i < relativeToLength; i++)
                {
                    if (PathInternal.IsDirectorySeparator(relativeTo[i]))
                    {
                        sb.Append(PathInternal.ParentDirectoryPrefix);
                    }
                }
            }
            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 count = pathLength - commonLength;

            if (pathEndsInSeparator)
            {
                count++;
            }

            sb.Append(path, commonLength, count);
            return(StringBuilderCache.GetStringAndRelease(sb));
        }
Пример #5
0
        public virtual string ReadString()
        {
            ThrowIfDisposed();

            int currPos = 0;
            int n;
            int stringLength;
            int readLength;
            int charsRead;

            // Length of the string in bytes, not chars
            stringLength = Read7BitEncodedInt();
            if (stringLength < 0)
            {
                throw new IOException(SR.Format(SR.IO_InvalidStringLen_Len, stringLength));
            }

            if (stringLength == 0)
            {
                return(string.Empty);
            }

            if (_charBytes == null)
            {
                _charBytes = new byte[MaxCharBytesSize];
            }

            if (_charBuffer == null)
            {
                _charBuffer = new char[_maxCharsSize];
            }

            StringBuilder?sb = null;

            do
            {
                readLength = ((stringLength - currPos) > MaxCharBytesSize) ? MaxCharBytesSize : (stringLength - currPos);

                n = _stream.Read(_charBytes, 0, readLength);
                if (n == 0)
                {
                    throw Error.GetEndOfFile();
                }

                charsRead = _decoder.GetChars(_charBytes, 0, n, _charBuffer, 0);

                if (currPos == 0 && n == stringLength)
                {
                    return(new string(_charBuffer, 0, charsRead));
                }

                if (sb == null)
                {
                    sb = StringBuilderCache.Acquire(stringLength); // Actual string length in chars may be smaller.
                }

                sb.Append(_charBuffer, 0, charsRead);
                currPos += n;
            } while (currPos < stringLength);

            return(StringBuilderCache.GetStringAndRelease(sb));
        }
Пример #6
0
        private static string CombineNoChecks(string path1, string path2, string path3, string path4)
        {
            if (path1.Length == 0)
            {
                return(CombineNoChecks(path2, path3, path4));
            }
            if (path2.Length == 0)
            {
                return(CombineNoChecks(path1, path3, path4));
            }
            if (path3.Length == 0)
            {
                return(CombineNoChecks(path1, path2, path4));
            }
            if (path4.Length == 0)
            {
                return(CombineNoChecks(path1, path2, path3));
            }

            if (IsPathRooted(path4))
            {
                return(path4);
            }
            if (IsPathRooted(path3))
            {
                return(CombineNoChecks(path3, path4));
            }
            if (IsPathRooted(path2))
            {
                return(CombineNoChecks(path2, path3, path4));
            }

            bool hasSep1 = PathInternal.IsDirectoryOrVolumeSeparator(path1[path1.Length - 1]);
            bool hasSep2 = PathInternal.IsDirectoryOrVolumeSeparator(path2[path2.Length - 1]);
            bool hasSep3 = PathInternal.IsDirectoryOrVolumeSeparator(path3[path3.Length - 1]);

            if (hasSep1 && hasSep2 && hasSep3)
            {
                // Use string.Concat overload that takes four strings
                return(path1 + path2 + path3 + path4);
            }
            else
            {
                // string.Concat only has string-based overloads up to four arguments; after that requires allocating
                // a params string[].  Instead, try to use a cached StringBuilder.
                StringBuilder sb = StringBuilderCache.Acquire(path1.Length + path2.Length + path3.Length + path4.Length + 3);

                sb.Append(path1);
                if (!hasSep1)
                {
                    sb.Append(PathInternal.DirectorySeparatorChar);
                }

                sb.Append(path2);
                if (!hasSep2)
                {
                    sb.Append(PathInternal.DirectorySeparatorChar);
                }

                sb.Append(path3);
                if (!hasSep3)
                {
                    sb.Append(PathInternal.DirectorySeparatorChar);
                }

                sb.Append(path4);

                return(StringBuilderCache.GetStringAndRelease(sb));
            }
        }
Пример #7
0
        /// <summary>
        /// Normalize separators in the given path. Converts forward slashes into back slashes and compresses slash runs, keeping initial 2 if present.
        /// Also trims initial whitespace in front of "rooted" paths (see PathStartSkip).
        ///
        /// This effectively replicates the behavior of the legacy NormalizePath when it was called with fullCheck=false and expandShortpaths=false.
        /// The current NormalizePath gets directory separator normalization from Win32's GetFullPathName(), which will resolve relative paths and as
        /// such can't be used here (and is overkill for our uses).
        ///
        /// Like the current NormalizePath this will not try and analyze periods/spaces within directory segments.
        /// </summary>
        /// <remarks>
        /// The only callers that used to use Path.Normalize(fullCheck=false) were Path.GetDirectoryName() and Path.GetPathRoot(). Both usages do
        /// not need trimming of trailing whitespace here.
        ///
        /// GetPathRoot() could technically skip normalizing separators after the second segment- consider as a future optimization.
        ///
        /// For legacy desktop behavior with ExpandShortPaths:
        ///  - It has no impact on GetPathRoot() so doesn't need consideration.
        ///  - It could impact GetDirectoryName(), but only if the path isn't relative (C:\ or \\Server\Share).
        ///
        /// In the case of GetDirectoryName() the ExpandShortPaths behavior was undocumented and provided inconsistent results if the path was
        /// fixed/relative. For example: "C:\PROGRA~1\A.TXT" would return "C:\Program Files" while ".\PROGRA~1\A.TXT" would return ".\PROGRA~1". If you
        /// ultimately call GetFullPath() this doesn't matter, but if you don't or have any intermediate string handling could easily be tripped up by
        /// this undocumented behavior.
        /// </remarks>
        internal static string NormalizeDirectorySeparators(string path)
        {
            if (string.IsNullOrEmpty(path))
            {
                return(path);
            }

            char current;
            int  start = PathStartSkip(path);

            if (start == 0)
            {
                // Make a pass to see if we need to normalize so we can potentially skip allocating
                bool normalized = true;

                for (int i = 0; i < path.Length; i++)
                {
                    current = path[i];
                    if (IsDirectorySeparator(current) &&
                        (current != Path.DirectorySeparatorChar
#if !PLATFORM_UNIX
                         // Check for sequential separators past the first position (we need to keep initial two for UNC/extended)
                         || (i > 0 && i + 1 < path.Length && IsDirectorySeparator(path[i + 1]))
#endif
                        ))
                    {
                        normalized = false;
                        break;
                    }
                }

                if (normalized)
                {
                    return(path);
                }
            }

            StringBuilder builder = StringBuilderCache.Acquire(path.Length);

#if !PLATFORM_UNIX
            // On Windows we always keep the first separator, even if the next is a separator (we need to keep initial two for UNC/extended)
            if (IsDirectorySeparator(path[start]))
            {
                start++;
                builder.Append(Path.DirectorySeparatorChar);
            }
#endif

            for (int i = start; i < path.Length; i++)
            {
                current = path[i];

                // If we have a separator
                if (IsDirectorySeparator(current))
                {
                    // If the next is a separator, skip adding this
                    if (i + 1 < path.Length && IsDirectorySeparator(path[i + 1]))
                    {
                        continue;
                    }

                    // Ensure it is the primary separator
                    current = Path.DirectorySeparatorChar;
                }

                builder.Append(current);
            }

            return(StringBuilderCache.GetStringAndRelease(builder));
        }
Пример #8
0
        static string _innerReadToNewlineDelimiter(this TextReader reader, string keyword, bool returnIfKeywordNotFound)
        {
            var  i = 0;
            var  j = 0;
            int  ci;
            var  sb        = StringBuilderCache.Acquire();
            var  delimiter = Environment.NewLine;
            bool secondHit = false;

            while ((ci = reader.Read()) != -1)
            {
                var c = (char)ci;
                if (c == delimiter[j])
                {
                    ++j;
                    if (j == delimiter.Length)
                    {
                        if (secondHit)
                        {
                            break;
                        }
                        else
                        {
                            while ((ci = reader.Read()) != -1 && ci != delimiter[0] && ((char)ci).IsWhiteSpace())
                            {
                                ;
                            }
                            if (ci == -1)
                            {
                                break;
                            }
                            else if (ci == delimiter[0])
                            {
                                secondHit = true;
                                j         = 1;
                                continue;
                            }
                            else
                            {
                                c = (char)ci;
                            }
                        }
                    }
                }
                else if (c == delimiter.Last())
                {
                    while ((ci = reader.Read()) != -1 && ci != delimiter[0] && ((char)ci).IsWhiteSpace())
                    {
                        ;
                    }
                    if (ci == -1)
                    {
                        break;
                    }
                    else if (ci == delimiter[0])
                    {
                        secondHit = true;
                        j         = 1;
                        continue;
                    }
                    else
                    {
                        c = (char)ci;
                    }
                }
                else
                {
                    if (c == delimiter[0])
                    {
                        j = 1;
                    }
                    else
                    {
                        j = 0;
                    }
                }

                if (c == keyword[i])
                {
                    ++i;
                    if (i == keyword.Length)
                    {
                        return(StringBuilderCache.GetStringAndRelease(sb));
                    }
                }
                else
                {
                    if (i != 0)
                    {
                        sb.Append(keyword.Substring(0, i));
                        i = 0;
                    }

                    if (c == keyword[0])
                    {
                        ++i;
                    }
                    else
                    {
                        sb.Append(c);
                    }
                }
            }

            if (returnIfKeywordNotFound)
            {
                return(StringBuilderCache.GetStringAndRelease(sb));
            }
            else
            {
                StringBuilderCache.Release(sb);
                return(null);
            }
        }
Пример #9
0
        public static string ReadTo(this TextReader reader, string keyword, string delimiter, bool returnIfKeywordNotFound = false)
        {
            if (delimiter == Environment.NewLine)
            {
                return(_innerReadToNewlineDelimiter(reader, keyword, returnIfKeywordNotFound));
            }
            var i = 0;
            var j = 0;
            int ci;
            var sb = StringBuilderCache.Acquire();

            while ((ci = reader.Read()) != -1)
            {
                var c = (char)ci;

                if (c == delimiter[j])
                {
                    ++j;
                    if (j == delimiter.Length)
                    {
                        break;
                    }
                }
                else
                {
                    if (c == delimiter[0])
                    {
                        j = 1;
                    }
                    else
                    {
                        j = 0;
                    }
                }

                if (c == keyword[i])
                {
                    ++i;
                    if (i == keyword.Length)
                    {
                        return(StringBuilderCache.GetStringAndRelease(sb));
                    }
                }
                else
                {
                    if (i != 0)
                    {
                        sb.Append(keyword.Substring(0, i));
                        i = 0;
                    }

                    if (c == keyword[0])
                    {
                        ++i;
                    }
                    else
                    {
                        sb.Append(c);
                    }
                }
            }

            if (returnIfKeywordNotFound)
            {
                return(StringBuilderCache.GetStringAndRelease(sb));
            }
            else
            {
                StringBuilderCache.Release(sb);
                return(null);
            }
        }