summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/IO/Path.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/src/System/IO/Path.cs')
-rw-r--r--src/mscorlib/src/System/IO/Path.cs1435
1 files changed, 0 insertions, 1435 deletions
diff --git a/src/mscorlib/src/System/IO/Path.cs b/src/mscorlib/src/System/IO/Path.cs
deleted file mode 100644
index 4f7993633b..0000000000
--- a/src/mscorlib/src/System/IO/Path.cs
+++ /dev/null
@@ -1,1435 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-/*============================================================
-**
-**
-**
-**
-**
-** Purpose: A collection of path manipulation methods.
-**
-**
-===========================================================*/
-
-using System;
-using System.Security.Permissions;
-using Win32Native = Microsoft.Win32.Win32Native;
-using System.Text;
-using System.Runtime.InteropServices;
-using System.Security;
-#if FEATURE_LEGACYSURFACE
-using System.Security.Cryptography;
-#endif
-using System.Runtime.CompilerServices;
-using System.Globalization;
-using System.Runtime.Versioning;
-using System.Diagnostics.Contracts;
-
-namespace System.IO {
- // Provides methods for processing directory strings in an ideally
- // cross-platform manner. Most of the methods don't do a complete
- // full parsing (such as examining a UNC hostname), but they will
- // handle most string operations.
- [ComVisible(true)]
- public static class Path
- {
- // Platform specific directory separator character. This is backslash
- // ('\') on Windows and slash ('/') on Unix.
- //
-#if !PLATFORM_UNIX
- public static readonly char DirectorySeparatorChar = '\\';
- internal const string DirectorySeparatorCharAsString = "\\";
-#else
- public static readonly char DirectorySeparatorChar = '/';
- internal const string DirectorySeparatorCharAsString = "/";
-#endif // !PLATFORM_UNIX
-
- // Platform specific alternate directory separator character.
- // There is only one directory separator char on Unix,
- // so the same definition is used for both Unix and Windows.
- public static readonly char AltDirectorySeparatorChar = '/';
-
- // Platform specific volume separator character. This is colon (':')
- // on Windows and MacOS, and slash ('/') on Unix. This is mostly
- // useful for parsing paths like "c:\windows" or "MacVolume:System Folder".
- //
-#if !PLATFORM_UNIX
- public static readonly char VolumeSeparatorChar = ':';
-#else
- public static readonly char VolumeSeparatorChar = '/';
-#endif // !PLATFORM_UNIX
-
- // Platform specific invalid list of characters in a path.
- // See the "Naming a File" MSDN conceptual docs for more details on
- // what is valid in a file name (which is slightly different from what
- // is legal in a path name).
- // Note: This list is duplicated in CheckInvalidPathChars
- [Obsolete("Please use GetInvalidPathChars or GetInvalidFileNameChars instead.")]
-#if !PLATFORM_UNIX
- public static readonly char[] InvalidPathChars = { '\"', '<', '>', '|', '\0', (Char)1, (Char)2, (Char)3, (Char)4, (Char)5, (Char)6, (Char)7, (Char)8, (Char)9, (Char)10, (Char)11, (Char)12, (Char)13, (Char)14, (Char)15, (Char)16, (Char)17, (Char)18, (Char)19, (Char)20, (Char)21, (Char)22, (Char)23, (Char)24, (Char)25, (Char)26, (Char)27, (Char)28, (Char)29, (Char)30, (Char)31 };
-#else
- public static readonly char[] InvalidPathChars = { '\0' };
-#endif // !PLATFORM_UNIX
-
- // Trim trailing white spaces, tabs etc but don't be aggressive in removing everything that has UnicodeCategory of trailing space.
- // String.WhitespaceChars will trim aggressively than what the underlying FS does (for ex, NTFS, FAT).
- internal static readonly char[] TrimEndChars =
- {
- (char)0x09, // Horizontal tab
- (char)0x0A, // Line feed
- (char)0x0B, // Vertical tab
- (char)0x0C, // Form feed
- (char)0x0D, // Carriage return
- (char)0x20, // Space
- (char)0x85, // Next line
- (char)0xA0 // Non breaking space
- };
-
-#if !PLATFORM_UNIX
- private static readonly char[] RealInvalidPathChars = PathInternal.InvalidPathChars;
-
- private static readonly char[] InvalidFileNameChars = { '\"', '<', '>', '|', '\0', (Char)1, (Char)2, (Char)3, (Char)4, (Char)5, (Char)6, (Char)7, (Char)8, (Char)9, (Char)10, (Char)11, (Char)12, (Char)13, (Char)14, (Char)15, (Char)16, (Char)17, (Char)18, (Char)19, (Char)20, (Char)21, (Char)22, (Char)23, (Char)24, (Char)25, (Char)26, (Char)27, (Char)28, (Char)29, (Char)30, (Char)31, ':', '*', '?', '\\', '/' };
-#else
- private static readonly char[] RealInvalidPathChars = { '\0' };
-
- private static readonly char[] InvalidFileNameChars = { '\0', '/' };
-#endif // !PLATFORM_UNIX
-
-#if !PLATFORM_UNIX
- public static readonly char PathSeparator = ';';
-#else
- public static readonly char PathSeparator = ':';
-#endif // !PLATFORM_UNIX
-
-
- // The max total path is 260, and the max individual component length is 255.
- // For example, D:\<256 char file name> isn't legal, even though it's under 260 chars.
- internal static readonly int MaxPath = PathInternal.MaxShortPath;
-
- internal static readonly int MaxPathComponentLength = PathInternal.MaxComponentLength;
-
- // Windows API definitions
- internal const int MAX_PATH = 260; // From WinDef.h
- internal const int MAX_DIRECTORY_PATH = 248; // cannot create directories greater than 248 characters
-
- // Changes the extension of a file path. The path parameter
- // specifies a file path, and the extension parameter
- // specifies a file extension (with a leading period, such as
- // ".exe" or ".cs").
- //
- // The function returns a file path with the same root, directory, and base
- // name parts as path, but with the file extension changed to
- // the specified extension. If path is null, the function
- // returns null. If path does not contain a file extension,
- // the new file extension is appended to the path. If extension
- // is null, any exsiting extension is removed from path.
- //
- public static String ChangeExtension(String path, String extension) {
- if (path != null) {
- CheckInvalidPathChars(path);
-
- String s = path;
- for (int i = path.Length; --i >= 0;) {
- char ch = path[i];
- if (ch == '.') {
- s = path.Substring(0, i);
- break;
- }
- if (ch == DirectorySeparatorChar || ch == AltDirectorySeparatorChar || ch == VolumeSeparatorChar) break;
- }
- if (extension != null && path.Length != 0) {
- if (extension.Length == 0 || extension[0] != '.') {
- s = s + ".";
- }
- s = s + extension;
- }
- return s;
- }
- return null;
- }
-
- // Returns the directory path of a file path. This method effectively
- // removes the last element of the given file path, i.e. it returns a
- // string consisting of all characters up to but not including the last
- // backslash ("\") in the file path. The returned value is null if the file
- // path is null or if the file path denotes a root (such as "\", "C:", or
- // "\\server\share").
- public static String GetDirectoryName(String path)
- {
- return GetDirectoryNameInternal(path);
- }
-
- [System.Security.SecuritySafeCritical]
- private static string GetDirectoryNameInternal(string path)
- {
- if (path != null)
- {
- CheckInvalidPathChars(path);
-
- // Expanding short paths is dangerous in this case as the results will change with the current directory.
- //
- // Suppose you have a path called "PICTUR~1\Foo". Now suppose you have two folders on disk "C:\Mine\Pictures Of Me"
- // and "C:\Yours\Pictures of You". If the current directory is neither you'll get back "PICTUR~1". If it is "C:\Mine"
- // get back "Pictures Of Me". "C:\Yours" would give back "Pictures of You".
- //
- // Because of this and as it isn't documented that short paths are expanded we will not expand short names unless
- // we're in legacy mode.
- string normalizedPath = NormalizePath(path, fullCheck: false, expandShortPaths:
-#if FEATURE_PATHCOMPAT
- AppContextSwitches.UseLegacyPathHandling
-#else
- false
-#endif
- );
-
- // If there are no permissions for PathDiscovery to this path, we should NOT expand the short paths
- // as this would leak information about paths to which the user would not have access to.
- if (path.Length > 0
-#if FEATURE_CAS_POLICY
- // Only do the extra logic if we're not in full trust
- && !CodeAccessSecurityEngine.QuickCheckForAllDemands()
-#endif
- )
- {
- try
- {
- // If we were passed in a path with \\?\ we need to remove it as FileIOPermission does not like it.
- string tempPath = RemoveLongPathPrefix(path);
-
- // FileIOPermission cannot handle paths that contain ? or *
- // So we only pass to FileIOPermission the text up to them.
- int pos = 0;
- while (pos < tempPath.Length && (tempPath[pos] != '?' && tempPath[pos] != '*'))
- pos++;
-
- // GetFullPath will Demand that we have the PathDiscovery FileIOPermission and thus throw
- // SecurityException if we don't.
- // While we don't use the result of this call we are using it as a consistent way of
- // doing the security checks.
- if (pos > 0)
- GetFullPath(tempPath.Substring(0, pos));
- }
- catch (SecurityException)
- {
- // If the user did not have permissions to the path, make sure that we don't leak expanded short paths
- // Only re-normalize if the original path had a ~ in it.
- if (path.IndexOf("~", StringComparison.Ordinal) != -1)
- {
- normalizedPath = NormalizePath(path, fullCheck: false, expandShortPaths: false);
- }
- }
- catch (PathTooLongException) { }
- catch (NotSupportedException) { } // Security can throw this on "c:\foo:"
- catch (IOException) { }
- catch (ArgumentException) { } // The normalizePath with fullCheck will throw this for file: and http:
- }
-
- path = normalizedPath;
-
- int root = GetRootLength(path);
- int i = path.Length;
- if (i > root)
- {
- i = path.Length;
- if (i == root) return null;
- while (i > root && path[--i] != DirectorySeparatorChar && path[i] != AltDirectorySeparatorChar);
- return path.Substring(0, i);
- }
- }
- return null;
- }
-
- // Gets the length of the root DirectoryInfo or whatever DirectoryInfo markers
- // are specified for the first part of the DirectoryInfo name.
- //
- internal static int GetRootLength(string path)
- {
- CheckInvalidPathChars(path);
-
-#if !PLATFORM_UNIX && FEATURE_PATHCOMPAT
- if (AppContextSwitches.UseLegacyPathHandling)
- {
- int i = 0;
- int length = path.Length;
-
- if (length >= 1 && (IsDirectorySeparator(path[0])))
- {
- // handles UNC names and directories off current drive's root.
- i = 1;
- if (length >= 2 && (IsDirectorySeparator(path[1])))
- {
- i = 2;
- int n = 2;
- while (i < length && ((path[i] != DirectorySeparatorChar && path[i] != AltDirectorySeparatorChar) || --n > 0)) i++;
- }
- }
- else if (length >= 2 && path[1] == VolumeSeparatorChar)
- {
- // handles A:\foo.
- i = 2;
- if (length >= 3 && (IsDirectorySeparator(path[2]))) i++;
- }
- return i;
- }
- else
-#endif // !PLATFORM_UNIX && FEATURE_PATHCOMPAT
- {
- return PathInternal.GetRootLength(path);
- }
- }
-
- internal static bool IsDirectorySeparator(char c) {
- return (c==DirectorySeparatorChar || c == AltDirectorySeparatorChar);
- }
-
- public static char[] GetInvalidPathChars()
- {
- return (char[]) RealInvalidPathChars.Clone();
- }
-
- public static char[] GetInvalidFileNameChars()
- {
- return (char[]) InvalidFileNameChars.Clone();
- }
-
- // Returns the extension of the given path. The returned value includes the
- // period (".") character of the extension except when you have a terminal period when you get String.Empty, such as ".exe" or
- // ".cpp". The returned value is null if the given path is
- // null or if the given path does not include an extension.
- //
- [Pure]
- public static String GetExtension(String path) {
- if (path==null)
- return null;
-
- CheckInvalidPathChars(path);
- int length = path.Length;
- for (int i = length; --i >= 0;) {
- char ch = path[i];
- if (ch == '.')
- {
- if (i != length - 1)
- return path.Substring(i, length - i);
- else
- return String.Empty;
- }
- if (ch == DirectorySeparatorChar || ch == AltDirectorySeparatorChar || ch == VolumeSeparatorChar)
- break;
- }
- return String.Empty;
- }
-
- // Expands the given path to a fully qualified path. The resulting string
- // consists of a drive letter, a colon, and a root relative path. This
- // function does not verify that the resulting path
- // refers to an existing file or directory on the associated volume.
- [Pure]
- [System.Security.SecuritySafeCritical]
- public static String GetFullPath(String path) {
- String fullPath = GetFullPathInternal(path);
-#if FEATURE_CORECLR
- FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, path, fullPath);
- state.EnsureState();
-#else
- FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, fullPath, false, false);
-#endif
- return fullPath;
- }
-
- [System.Security.SecurityCritical]
- internal static String UnsafeGetFullPath(String path)
- {
- String fullPath = GetFullPathInternal(path);
-#if !FEATURE_CORECLR
- FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, fullPath, false, false);
-#endif
- return fullPath;
- }
-
- // This method is package access to let us quickly get a string name
- // while avoiding a security check. This also serves a slightly
- // different purpose - when we open a file, we need to resolve the
- // path into a fully qualified, non-relative path name. This
- // method does that, finding the current drive &; directory. But
- // as long as we don't return this info to the user, we're good. However,
- // the public GetFullPath does need to do a security check.
- internal static string GetFullPathInternal(string path)
- {
- if (path == null)
- throw new ArgumentNullException("path");
- Contract.EndContractBlock();
-
- string newPath = NormalizePath(path, fullCheck: true);
- return newPath;
- }
-
- [System.Security.SecuritySafeCritical] // auto-generated
- internal unsafe static string NormalizePath(string path, bool fullCheck)
- {
- return NormalizePath(path, fullCheck,
-#if FEATURE_PATHCOMPAT
- AppContextSwitches.BlockLongPaths ? PathInternal.MaxShortPath :
-#endif
- PathInternal.MaxLongPath);
- }
-
- [System.Security.SecuritySafeCritical] // auto-generated
- internal unsafe static string NormalizePath(string path, bool fullCheck, bool expandShortPaths)
- {
- return NormalizePath(path, fullCheck,
-#if FEATURE_PATHCOMPAT
- AppContextSwitches.BlockLongPaths ? PathInternal.MaxShortPath :
-#endif
- PathInternal.MaxLongPath,
- expandShortPaths);
- }
-
- [System.Security.SecuritySafeCritical] // auto-generated
- internal static string NormalizePath(string path, bool fullCheck, int maxPathLength)
- {
- return NormalizePath(path, fullCheck, maxPathLength, expandShortPaths: true);
- }
-
- [System.Security.SecuritySafeCritical]
- internal static string NormalizePath(string path, bool fullCheck, int maxPathLength, bool expandShortPaths)
- {
-#if FEATURE_PATHCOMPAT
- if (AppContextSwitches.UseLegacyPathHandling)
- {
- return LegacyNormalizePath(path, fullCheck, maxPathLength, expandShortPaths);
- }
- else
-#endif // FEATURE_APPCOMPAT
- {
- if (PathInternal.IsExtended(path))
- {
- // We can't really know what is valid for all cases of extended paths.
- //
- // - object names can include other characters as well (':', '/', etc.)
- // - even file objects have different rules (pipe names can contain most characters)
- //
- // As such we will do no further analysis of extended paths to avoid blocking known and unknown
- // scenarios as well as minimizing compat breaks should we block now and need to unblock later.
- return path;
- }
-
- string normalizedPath = null;
-
- if (fullCheck == false)
- {
- // Disabled fullCheck is only called by GetDirectoryName and GetPathRoot.
- // Avoid adding addtional callers and try going direct to lighter weight NormalizeDirectorySeparators.
- normalizedPath = NewNormalizePathLimitedChecks(path, maxPathLength, expandShortPaths);
- }
- else
- {
- normalizedPath = NewNormalizePath(path, maxPathLength, expandShortPaths: true);
- }
-
- if (string.IsNullOrWhiteSpace(normalizedPath))
- throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal"));
- return normalizedPath;
- }
- }
-
- [System.Security.SecuritySafeCritical]
- private static string NewNormalizePathLimitedChecks(string path, int maxPathLength, bool expandShortPaths)
- {
- string normalized = PathInternal.NormalizeDirectorySeparators(path);
-
- if (PathInternal.IsPathTooLong(normalized) || PathInternal.AreSegmentsTooLong(normalized))
- throw new PathTooLongException();
-
-#if !PLATFORM_UNIX
- if (!PathInternal.IsDevice(normalized) && PathInternal.HasInvalidVolumeSeparator(path))
- throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal"));
-
- if (expandShortPaths && normalized.IndexOf('~') != -1)
- {
- try
- {
- return LongPathHelper.GetLongPathName(normalized);
- }
- catch
- {
- // Don't care if we can't get the long path- might not exist, etc.
- }
- }
-#endif
-
- return normalized;
- }
-
- /// <summary>
- /// Normalize the path and check for bad characters or other invalid syntax.
- /// </summary>
- [System.Security.SecuritySafeCritical]
- [ResourceExposure(ResourceScope.Machine)]
- [ResourceConsumption(ResourceScope.Machine)]
- private static string NewNormalizePath(string path, int maxPathLength, bool expandShortPaths)
- {
- Contract.Requires(path != null, "path can't be null");
-
- // Embedded null characters are the only invalid character case we want to check up front.
- // This is because the nulls will signal the end of the string to Win32 and therefore have
- // unpredictable results. Other invalid characters we give a chance to be normalized out.
- if (path.IndexOf('\0') != -1)
- throw new ArgumentException(Environment.GetResourceString("Argument_InvalidPathChars"));
-
-#if !PLATFORM_UNIX
- // Note that colon and wildcard checks happen in FileIOPermissions
-
- // Technically this doesn't matter but we used to throw for this case
- if (string.IsNullOrWhiteSpace(path))
- throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal"));
-
- // We don't want to check invalid characters for device format- see comments for extended above
- return LongPathHelper.Normalize(path, (uint)maxPathLength, checkInvalidCharacters: !PathInternal.IsDevice(path), expandShortPaths: expandShortPaths);
-#else
- if (path.Length == 0)
- throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal"));
-
- // Expand with current directory if necessary
- if (!IsPathRooted(path))
- path = Combine(Directory.GetCurrentDirectory(), path);
-
- // We would ideally use realpath to do this, but it resolves symlinks, requires that the file actually exist,
- // and turns it into a full path, which we only want if fullCheck is true.
- string collapsedString = PathInternal.RemoveRelativeSegments(path);
-
- if (collapsedString.Length > maxPathLength)
- throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong"));
-
- return collapsedString.Length == 0 ? "/" : collapsedString;
-#endif // PLATFORM_UNIX
- }
-
-#if FEATURE_PATHCOMPAT
- [System.Security.SecurityCritical] // auto-generated
- internal unsafe static String LegacyNormalizePath(String path, bool fullCheck, int maxPathLength, bool expandShortPaths) {
-
- Contract.Requires(path != null, "path can't be null");
- // If we're doing a full path check, trim whitespace and look for
- // illegal path characters.
- if (fullCheck) {
- // Trim whitespace off the end of the string.
- // Win32 normalization trims only U+0020.
- path = path.TrimEnd(TrimEndChars);
-
- // Look for illegal path characters.
- if (PathInternal.AnyPathHasIllegalCharacters(path))
- throw new ArgumentException(Environment.GetResourceString("Argument_InvalidPathChars"));
- }
-
- int index = 0;
- // We prefer to allocate on the stack for workingset/perf gain. If the
- // starting path is less than MaxPath then we can stackalloc; otherwise we'll
- // use a StringBuilder (PathHelper does this under the hood). The latter may
- // happen in 2 cases:
- // 1. Starting path is greater than MaxPath but it normalizes down to MaxPath.
- // This is relevant for paths containing escape sequences. In this case, we
- // attempt to normalize down to MaxPath, but the caller pays a perf penalty
- // since StringBuilder is used.
- // 2. IsolatedStorage, which supports paths longer than MaxPath (value given
- // by maxPathLength.
- PathHelper newBuffer;
- if (path.Length + 1 <= MaxPath) {
- char* m_arrayPtr = stackalloc char[MaxPath];
- newBuffer = new PathHelper(m_arrayPtr, MaxPath);
- } else {
- newBuffer = new PathHelper(path.Length + Path.MaxPath, maxPathLength);
- }
-
- uint numSpaces = 0;
- uint numDots = 0;
- bool fixupDirectorySeparator = false;
- // Number of significant chars other than potentially suppressible
- // dots and spaces since the last directory or volume separator char
- uint numSigChars = 0;
- int lastSigChar = -1; // Index of last significant character.
- // Whether this segment of the path (not the complete path) started
- // with a volume separator char. Reject "c:...".
- bool startedWithVolumeSeparator = false;
- bool firstSegment = true;
- int lastDirectorySeparatorPos = 0;
-
-#if !PLATFORM_UNIX
- bool mightBeShortFileName = false;
-
- // LEGACY: This code is here for backwards compatibility reasons. It
- // ensures that \\foo.cs\bar.cs stays \\foo.cs\bar.cs instead of being
- // turned into \foo.cs\bar.cs.
- if (path.Length > 0 && (path[0] == DirectorySeparatorChar || path[0] == AltDirectorySeparatorChar)) {
- newBuffer.Append('\\');
- index++;
- lastSigChar = 0;
- }
-#endif
-
- // Normalize the string, stripping out redundant dots, spaces, and
- // slashes.
- while (index < path.Length) {
- char currentChar = path[index];
-
- // We handle both directory separators and dots specially. For
- // directory separators, we consume consecutive appearances.
- // For dots, we consume all dots beyond the second in
- // succession. All other characters are added as is. In
- // addition we consume all spaces after the last other char
- // in a directory name up until the directory separator.
-
- if (currentChar == DirectorySeparatorChar || currentChar == AltDirectorySeparatorChar) {
- // If we have a path like "123.../foo", remove the trailing dots.
- // However, if we found "c:\temp\..\bar" or "c:\temp\...\bar", don't.
- // Also remove trailing spaces from both files & directory names.
- // This was agreed on with the OS team to fix undeletable directory
- // names ending in spaces.
-
- // If we saw a '\' as the previous last significant character and
- // are simply going to write out dots, suppress them.
- // If we only contain dots and slashes though, only allow
- // a string like [dot]+ [space]*. Ignore everything else.
- // Legal: "\.. \", "\...\", "\. \"
- // Illegal: "\.. .\", "\. .\", "\ .\"
- if (numSigChars == 0) {
- // Dot and space handling
- if (numDots > 0) {
- // Look for ".[space]*" or "..[space]*"
- int start = lastSigChar + 1;
- if (path[start] != '.')
- throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal"));
-
- // Only allow "[dot]+[space]*", and normalize the
- // legal ones to "." or ".."
- if (numDots >= 2) {
- // Reject "C:..."
- if (startedWithVolumeSeparator && numDots > 2)
-
- throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal"));
-
- if (path[start + 1] == '.') {
- // Search for a space in the middle of the
- // dots and throw
- for(int i=start + 2; i < start + numDots; i++) {
- if (path[i] != '.')
- throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal"));
- }
-
- numDots = 2;
- }
- else {
- if (numDots > 1)
- throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal"));
- numDots = 1;
- }
- }
-
- if (numDots == 2) {
- newBuffer.Append('.');
- }
-
- newBuffer.Append('.');
- fixupDirectorySeparator = false;
-
- // Continue in this case, potentially writing out '\'.
- }
-
- if (numSpaces > 0 && firstSegment) {
- // Handle strings like " \\server\share".
- if (index + 1 < path.Length &&
- (path[index + 1] == DirectorySeparatorChar || path[index + 1] == AltDirectorySeparatorChar))
- {
- newBuffer.Append(DirectorySeparatorChar);
- }
- }
- }
- numDots = 0;
- numSpaces = 0; // Suppress trailing spaces
-
- if (!fixupDirectorySeparator) {
- fixupDirectorySeparator = true;
- newBuffer.Append(DirectorySeparatorChar);
- }
- numSigChars = 0;
- lastSigChar = index;
- startedWithVolumeSeparator = false;
- firstSegment = false;
-
-#if !PLATFORM_UNIX
- // For short file names, we must try to expand each of them as
- // soon as possible. We need to allow people to specify a file
- // name that doesn't exist using a path with short file names
- // in it, such as this for a temp file we're trying to create:
- // C:\DOCUME~1\USERNA~1.RED\LOCALS~1\Temp\bg3ylpzp
- // We could try doing this afterwards piece by piece, but it's
- // probably a lot simpler to do it here.
- if (mightBeShortFileName) {
- newBuffer.TryExpandShortFileName();
- mightBeShortFileName = false;
- }
-#endif
- int thisPos = newBuffer.Length - 1;
- if (thisPos - lastDirectorySeparatorPos > MaxPathComponentLength)
- {
- throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong"));
- }
- lastDirectorySeparatorPos = thisPos;
- } // if (Found directory separator)
- else if (currentChar == '.') {
- // Reduce only multiple .'s only after slash to 2 dots. For
- // instance a...b is a valid file name.
- numDots++;
- // Don't flush out non-terminal spaces here, because they may in
- // the end not be significant. Turn "c:\ . .\foo" -> "c:\foo"
- // which is the conclusion of removing trailing dots & spaces,
- // as well as folding multiple '\' characters.
- }
- else if (currentChar == ' ') {
- numSpaces++;
- }
- else { // Normal character logic
-#if !PLATFORM_UNIX
- if (currentChar == '~' && expandShortPaths)
- mightBeShortFileName = true;
-#endif
-
- fixupDirectorySeparator = false;
-
-#if !PLATFORM_UNIX
- // To reject strings like "C:...\foo" and "C :\foo"
- if (firstSegment && currentChar == VolumeSeparatorChar) {
- // Only accept "C:", not "c :" or ":"
- // Get a drive letter or ' ' if index is 0.
- char driveLetter = (index > 0) ? path[index-1] : ' ';
- bool validPath = ((numDots == 0) && (numSigChars >= 1) && (driveLetter != ' '));
- if (!validPath)
- throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal"));
-
- startedWithVolumeSeparator = true;
- // We need special logic to make " c:" work, we should not fix paths like " foo::$DATA"
- if (numSigChars > 1) { // Common case, simply do nothing
- int spaceCount = 0; // How many spaces did we write out, numSpaces has already been reset.
- while((spaceCount < newBuffer.Length) && newBuffer[spaceCount] == ' ')
- spaceCount++;
- if (numSigChars - spaceCount == 1) {
- //Safe to update stack ptr directly
- newBuffer.Length = 0;
- newBuffer.Append(driveLetter); // Overwrite spaces, we need a special case to not break " foo" as a relative path.
- }
- }
- numSigChars = 0;
- }
- else
-#endif // !PLATFORM_UNIX
- {
- numSigChars += 1 + numDots + numSpaces;
- }
-
- // Copy any spaces & dots since the last significant character
- // to here. Note we only counted the number of dots & spaces,
- // and don't know what order they're in. Hence the copy.
- if (numDots > 0 || numSpaces > 0) {
- int numCharsToCopy = (lastSigChar >= 0) ? index - lastSigChar - 1 : index;
- if (numCharsToCopy > 0) {
- for (int i=0; i<numCharsToCopy; i++) {
- newBuffer.Append(path[lastSigChar + 1 + i]);
- }
- }
- numDots = 0;
- numSpaces = 0;
- }
-
- newBuffer.Append(currentChar);
- lastSigChar = index;
- }
-
- index++;
- } // end while
-
- if (newBuffer.Length - 1 - lastDirectorySeparatorPos > MaxPathComponentLength)
- {
- throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong"));
- }
-
- // Drop any trailing dots and spaces from file & directory names, EXCEPT
- // we MUST make sure that "C:\foo\.." is correctly handled.
- // Also handle "C:\foo\." -> "C:\foo", while "C:\." -> "C:\"
- if (numSigChars == 0) {
- if (numDots > 0) {
- // Look for ".[space]*" or "..[space]*"
- int start = lastSigChar + 1;
- if (path[start] != '.')
- throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal"));
-
- // Only allow "[dot]+[space]*", and normalize the
- // legal ones to "." or ".."
- if (numDots >= 2) {
- // Reject "C:..."
- if (startedWithVolumeSeparator && numDots > 2)
- throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal"));
-
- if (path[start + 1] == '.') {
- // Search for a space in the middle of the
- // dots and throw
- for(int i=start + 2; i < start + numDots; i++) {
- if (path[i] != '.')
- throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal"));
- }
-
- numDots = 2;
- }
- else {
- if (numDots > 1)
- throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal"));
- numDots = 1;
- }
- }
-
- if (numDots == 2) {
- newBuffer.Append('.');
- }
-
- newBuffer.Append('.');
- }
- } // if (numSigChars == 0)
-
- // If we ended up eating all the characters, bail out.
- if (newBuffer.Length == 0)
- throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal"));
-
- // Disallow URL's here. Some of our other Win32 API calls will reject
- // them later, so we might be better off rejecting them here.
- // Note we've probably turned them into "file:\D:\foo.tmp" by now.
- // But for compatibility, ensure that callers that aren't doing a
- // full check aren't rejected here.
- if (fullCheck) {
- if ( newBuffer.OrdinalStartsWith("http:", false) ||
- newBuffer.OrdinalStartsWith("file:", false))
- {
- throw new ArgumentException(Environment.GetResourceString("Argument_PathUriFormatNotSupported"));
- }
- }
-
-#if !PLATFORM_UNIX
- // If the last part of the path (file or directory name) had a tilde,
- // expand that too.
- if (mightBeShortFileName) {
- newBuffer.TryExpandShortFileName();
- }
-#endif
-
- // Call the Win32 API to do the final canonicalization step.
- int result = 1;
-
- if (fullCheck) {
- // NOTE: Win32 GetFullPathName requires the input buffer to be big enough to fit the initial
- // path which is a concat of CWD and the relative path, this can be of an arbitrary
- // size and could be > MAX_PATH (which becomes an artificial limit at this point),
- // even though the final normalized path after fixing up the relative path syntax
- // might be well within the MAX_PATH restriction. For ex,
- // "c:\SomeReallyLongDirName(thinkGreaterThan_MAXPATH)\..\foo.txt" which actually requires a
- // buffer well with in the MAX_PATH as the normalized path is just "c:\foo.txt"
- // This buffer requirement seems wrong, it could be a bug or a perf optimization
- // like returning required buffer length quickly or avoid stratch buffer etc.
- // Ideally we would get the required buffer length first by calling GetFullPathName
- // once without the buffer and use that in the later call but this doesn't always work
- // due to Win32 GetFullPathName bug. For instance, in Win2k, when the path we are trying to
- // fully qualify is a single letter name (such as "a", "1", ",") GetFullPathName
- // fails to return the right buffer size (i.e, resulting in insufficient buffer).
- // To workaround this bug we will start with MAX_PATH buffer and grow it once if the
- // return value is > MAX_PATH.
-
- result = newBuffer.GetFullPathName();
-
-#if !PLATFORM_UNIX
- // If we called GetFullPathName with something like "foo" and our
- // command window was in short file name mode (ie, by running edlin or
- // DOS versions of grep, etc), we might have gotten back a short file
- // name. So, check to see if we need to expand it.
- mightBeShortFileName = false;
- for(int i=0; i < newBuffer.Length && !mightBeShortFileName; i++) {
- if (newBuffer[i] == '~' && expandShortPaths)
- mightBeShortFileName = true;
- }
-
- if (mightBeShortFileName) {
- bool r = newBuffer.TryExpandShortFileName();
- // Consider how the path "Doesn'tExist" would expand. If
- // we add in the current directory, it too will need to be
- // fully expanded, which doesn't happen if we use a file
- // name that doesn't exist.
- if (!r) {
- int lastSlash = -1;
-
- for (int i = newBuffer.Length - 1; i >= 0; i--) {
- if (newBuffer[i] == DirectorySeparatorChar) {
- lastSlash = i;
- break;
- }
- }
-
- if (lastSlash >= 0) {
-
- // This bounds check is for safe memcpy but we should never get this far
- if (newBuffer.Length >= maxPathLength)
- throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong"));
-
- int lenSavedName = newBuffer.Length - lastSlash - 1;
- Contract.Assert(lastSlash < newBuffer.Length, "path unexpectedly ended in a '\'");
-
- newBuffer.Fixup(lenSavedName, lastSlash);
- }
- }
- }
-#endif // PLATFORM_UNIX
- }
-
- if (result != 0) {
- /* Throw an ArgumentException for paths like \\, \\server, \\server\
- This check can only be properly done after normalizing, so
- \\foo\.. will be properly rejected. Also, reject \\?\GLOBALROOT\
- (an internal kernel path) because it provides aliases for drives. */
- if (newBuffer.Length > 1 && newBuffer[0] == '\\' && newBuffer[1] == '\\') {
- int startIndex = 2;
- while (startIndex < result) {
- if (newBuffer[startIndex] == '\\') {
- startIndex++;
- break;
- }
- else {
- startIndex++;
- }
- }
- if (startIndex == result)
- throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegalUNC"));
-
- // Check for \\?\Globalroot, an internal mechanism to the kernel
- // that provides aliases for drives and other undocumented stuff.
- // The kernel team won't even describe the full set of what
- // is available here - we don't want managed apps mucking
- // with this for security reasons.
- if ( newBuffer.OrdinalStartsWith("\\\\?\\globalroot", true))
- throw new ArgumentException(Environment.GetResourceString("Arg_PathGlobalRoot"));
- }
- }
-
- // Check our result and form the managed string as necessary.
- if (newBuffer.Length >= maxPathLength)
- throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong"));
-
- if (result == 0) {
- int errorCode = Marshal.GetLastWin32Error();
- if (errorCode == 0)
- errorCode = Win32Native.ERROR_BAD_PATHNAME;
- __Error.WinIOError(errorCode, path);
- return null; // Unreachable - silence a compiler error.
- }
-
- return newBuffer.ToStringOrExisting(path);
- }
-#endif // FEATURE_PATHCOMPAT
-
- internal const int MaxLongPath = PathInternal.MaxLongPath;
-
- private const string LongPathPrefix = PathInternal.ExtendedPathPrefix;
- private const string UNCPathPrefix = PathInternal.UncPathPrefix;
- private const string UNCLongPathPrefixToInsert = PathInternal.UncExtendedPrefixToInsert;
- private const string UNCLongPathPrefix = PathInternal.UncExtendedPathPrefix;
-
- internal static bool HasLongPathPrefix(string path)
- {
-#if FEATURE_PATHCOMPAT
- if (AppContextSwitches.UseLegacyPathHandling)
- return path.StartsWith(LongPathPrefix, StringComparison.Ordinal);
- else
-#endif
- return PathInternal.IsExtended(path);
- }
-
- internal static string AddLongPathPrefix(string path)
- {
-#if FEATURE_PATHCOMPAT
- if (AppContextSwitches.UseLegacyPathHandling)
- {
- if (path.StartsWith(LongPathPrefix, StringComparison.Ordinal))
- return path;
-
- if (path.StartsWith(UNCPathPrefix, StringComparison.Ordinal))
- return path.Insert(2, UNCLongPathPrefixToInsert); // Given \\server\share in longpath becomes \\?\UNC\server\share => UNCLongPathPrefix + path.SubString(2); => The actual command simply reduces the operation cost.
-
- return LongPathPrefix + path;
- }
- else
-#endif
- {
- return PathInternal.EnsureExtendedPrefix(path);
- }
- }
-
- internal static string RemoveLongPathPrefix(string path)
- {
-#if FEATURE_PATHCOMPAT
- if (AppContextSwitches.UseLegacyPathHandling)
- {
- if (!path.StartsWith(LongPathPrefix, StringComparison.Ordinal))
- return path;
-
- if (path.StartsWith(UNCLongPathPrefix, StringComparison.OrdinalIgnoreCase))
- return path.Remove(2, 6); // Given \\?\UNC\server\share we return \\server\share => @'\\' + path.SubString(UNCLongPathPrefix.Length) => The actual command simply reduces the operation cost.
-
- return path.Substring(4);
- }
- else
-#endif
- {
- return PathInternal.RemoveExtendedPrefix(path);
- }
- }
-
- internal static StringBuilder RemoveLongPathPrefix(StringBuilder pathSB)
- {
-#if FEATURE_PATHCOMPAT
- if (AppContextSwitches.UseLegacyPathHandling)
- {
- if (!PathInternal.StartsWithOrdinal(pathSB, LongPathPrefix))
- return pathSB;
-
- // Given \\?\UNC\server\share we return \\server\share => @'\\' + path.SubString(UNCLongPathPrefix.Length) => The actual command simply reduces the operation cost.
- if (PathInternal.StartsWithOrdinal(pathSB, UNCLongPathPrefix, ignoreCase: true))
- return pathSB.Remove(2, 6);
-
- return pathSB.Remove(0, 4);
- }
- else
-#endif
- {
- return PathInternal.RemoveExtendedPrefix(pathSB);
- }
- }
-
-
- // Returns the name and extension parts of the given path. The resulting
- // string contains the characters of path that follow the last
- // backslash ("\"), slash ("/"), or colon (":") character in
- // path. The resulting string is the entire path if path
- // contains no backslash after removing trailing slashes, slash, or colon characters. The resulting
- // string is null if path is null.
- //
- [Pure]
- public static String GetFileName(String path) {
- if (path != null) {
- CheckInvalidPathChars(path);
-
- int length = path.Length;
- for (int i = length; --i >= 0;) {
- char ch = path[i];
- if (ch == DirectorySeparatorChar || ch == AltDirectorySeparatorChar || ch == VolumeSeparatorChar)
- return path.Substring(i + 1, length - i - 1);
-
- }
- }
- return path;
- }
-
- [Pure]
- public static String GetFileNameWithoutExtension(String path) {
- path = GetFileName(path);
- if (path != null)
- {
- int i;
- if ((i=path.LastIndexOf('.')) == -1)
- return path; // No path extension found
- else
- return path.Substring(0,i);
- }
- return null;
- }
-
-
-
- // Returns the root portion of the given path. The resulting string
- // consists of those rightmost characters of the path that constitute the
- // root of the path. Possible patterns for the resulting string are: An
- // empty string (a relative path on the current drive), "\" (an absolute
- // path on the current drive), "X:" (a relative path on a given drive,
- // where X is the drive letter), "X:\" (an absolute path on a given drive),
- // and "\\server\share" (a UNC path for a given server and share name).
- // The resulting string is null if path is null.
- //
- [Pure]
- public static String GetPathRoot(String path) {
- if (path == null) return null;
-
- // Expanding short paths has no impact on the path root- there is no such thing as an
- // 8.3 volume or server/share name.
- path = NormalizePath(path, fullCheck: false, expandShortPaths: false);
- return path.Substring(0, GetRootLength(path));
- }
-
- [System.Security.SecuritySafeCritical]
- public static String GetTempPath()
- {
-#if !FEATURE_CORECLR
- new EnvironmentPermission(PermissionState.Unrestricted).Demand();
-#endif
- StringBuilder sb = new StringBuilder(PathInternal.MaxShortPath);
- uint r = Win32Native.GetTempPath(PathInternal.MaxShortPath, sb);
- String path = sb.ToString();
- if (r==0) __Error.WinIOError();
- path = GetFullPathInternal(path);
-#if FEATURE_CORECLR
- FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Write, String.Empty, path);
- state.EnsureState();
-#endif
- return path;
- }
-
- internal static bool IsRelative(string path)
- {
- Contract.Assert(path != null, "path can't be null");
- return PathInternal.IsPartiallyQualified(path);
- }
-
- // Returns a cryptographically strong random 8.3 string that can be
- // used as either a folder name or a file name.
-#if FEATURE_CORECLR
- [System.Security.SecuritySafeCritical]
-#endif
- public static String GetRandomFileName()
- {
- // 5 bytes == 40 bits == 40/5 == 8 chars in our encoding
- // This gives us exactly 8 chars. We want to avoid the 8.3 short name issue
- byte[] key = new byte[10];
-
-#if FEATURE_CORECLR
- Win32Native.Random(true, key, key.Length);
-#else
- // RNGCryptoServiceProvider is disposable in post-Orcas desktop mscorlibs, but not in CoreCLR's
- // mscorlib, so we need to do a manual using block for it.
- RNGCryptoServiceProvider rng = null;
- try
- {
- rng = new RNGCryptoServiceProvider();
-
- rng.GetBytes(key);
- }
- finally
- {
- if (rng != null)
- {
- rng.Dispose();
- }
- }
-#endif
-
- // rndCharArray is expected to be 16 chars
- char[] rndCharArray = Path.ToBase32StringSuitableForDirName(key).ToCharArray();
- rndCharArray[8] = '.';
- return new String(rndCharArray, 0, 12);
- }
-
- // Returns a unique temporary file name, and creates a 0-byte file by that
- // name on disk.
- [System.Security.SecuritySafeCritical]
- public static String GetTempFileName()
- {
- return InternalGetTempFileName(true);
- }
-
- [System.Security.SecurityCritical]
- internal static String UnsafeGetTempFileName()
- {
- return InternalGetTempFileName(false);
- }
-
- [System.Security.SecurityCritical]
- private static String InternalGetTempFileName(bool checkHost)
- {
- String path = GetTempPath();
-
- // Since this can write to the temp directory and theoretically
- // cause a denial of service attack, demand FileIOPermission to
- // that directory.
-
-#if FEATURE_CORECLR
- if (checkHost)
- {
- FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Write, String.Empty, path);
- state.EnsureState();
- }
-#else
- FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, path);
-#endif
- StringBuilder sb = new StringBuilder(MaxPath);
- uint r = Win32Native.GetTempFileName(path, "tmp", 0, sb);
- if (r==0) __Error.WinIOError();
- return sb.ToString();
- }
-
- // Tests if a path includes a file extension. The result is
- // true if the characters that follow the last directory
- // separator ('\\' or '/') or volume separator (':') in the path include
- // a period (".") other than a terminal period. The result is false otherwise.
- //
- [Pure]
- public static bool HasExtension(String path) {
- if (path != null) {
- CheckInvalidPathChars(path);
-
- for (int i = path.Length; --i >= 0;) {
- char ch = path[i];
- if (ch == '.') {
- if ( i != path.Length - 1)
- return true;
- else
- return false;
- }
- if (ch == DirectorySeparatorChar || ch == AltDirectorySeparatorChar || ch == VolumeSeparatorChar) break;
- }
- }
- return false;
- }
-
- // Tests if the given path contains a root. A path is considered rooted
- // if it starts with a backslash ("\") or a drive letter and a colon (":").
- //
- [Pure]
- public static bool IsPathRooted(String path) {
- if (path != null) {
- CheckInvalidPathChars(path);
-
- int length = path.Length;
-#if !PLATFORM_UNIX
- if ((length >= 1 && (path[0] == DirectorySeparatorChar || path[0] == AltDirectorySeparatorChar)) || (length >= 2 && path[1] == VolumeSeparatorChar))
- return true;
-#else
- if (length >= 1 && (path[0] == DirectorySeparatorChar || path[0] == AltDirectorySeparatorChar))
- return true;
-#endif
- }
- return false;
- }
-
- public static String Combine(String path1, String path2) {
- if (path1==null || path2==null)
- throw new ArgumentNullException((path1==null) ? "path1" : "path2");
- Contract.EndContractBlock();
- CheckInvalidPathChars(path1);
- CheckInvalidPathChars(path2);
-
- return CombineNoChecks(path1, path2);
- }
-
- public static String Combine(String path1, String path2, String path3) {
- if (path1 == null || path2 == null || path3 == null)
- throw new ArgumentNullException((path1 == null) ? "path1" : (path2 == null) ? "path2" : "path3");
- Contract.EndContractBlock();
- CheckInvalidPathChars(path1);
- CheckInvalidPathChars(path2);
- CheckInvalidPathChars(path3);
-
- return CombineNoChecks(CombineNoChecks(path1, path2), path3);
- }
-
- public static String Combine(String path1, String path2, String path3, String path4) {
- if (path1 == null || path2 == null || path3 == null || path4 == null)
- throw new ArgumentNullException((path1 == null) ? "path1" : (path2 == null) ? "path2" : (path3 == null) ? "path3" : "path4");
- Contract.EndContractBlock();
- CheckInvalidPathChars(path1);
- CheckInvalidPathChars(path2);
- CheckInvalidPathChars(path3);
- CheckInvalidPathChars(path4);
-
- return CombineNoChecks(CombineNoChecks(CombineNoChecks(path1, path2), path3), path4);
- }
-
- public static String Combine(params String[] paths) {
- if (paths == null) {
- throw new ArgumentNullException("paths");
- }
- Contract.EndContractBlock();
-
- int finalSize = 0;
- int firstComponent = 0;
-
- // We have two passes, the first calcuates 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("paths");
- }
-
- if (paths[i].Length == 0) {
- continue;
- }
-
- CheckInvalidPathChars(paths[i]);
-
- if (Path.IsPathRooted(paths[i])) {
- firstComponent = i;
- finalSize = paths[i].Length;
- } else {
- finalSize += paths[i].Length;
- }
-
- char ch = paths[i][paths[i].Length - 1];
- if (ch != DirectorySeparatorChar && ch != AltDirectorySeparatorChar && ch != VolumeSeparatorChar)
- 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 (ch != DirectorySeparatorChar && ch != AltDirectorySeparatorChar && ch != VolumeSeparatorChar) {
- finalPath.Append(DirectorySeparatorChar);
- }
-
- finalPath.Append(paths[i]);
- }
- }
-
- return StringBuilderCache.GetStringAndRelease(finalPath);
- }
-
- private static String CombineNoChecks(String path1, String path2) {
- if (path2.Length == 0)
- return path1;
-
- if (path1.Length == 0)
- return path2;
-
- if (IsPathRooted(path2))
- return path2;
-
- char ch = path1[path1.Length - 1];
- if (ch != DirectorySeparatorChar && ch != AltDirectorySeparatorChar && ch != VolumeSeparatorChar)
- return path1 + DirectorySeparatorCharAsString + path2;
- return path1 + path2;
- }
-
- private static readonly Char[] s_Base32Char = {
- 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
- 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
- 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
- 'y', 'z', '0', '1', '2', '3', '4', '5'};
-
- internal static String ToBase32StringSuitableForDirName(byte[] buff)
- {
- // This routine is optimised to be used with buffs of length 20
- Contract.Assert(((buff.Length % 5) == 0), "Unexpected hash length");
-
- StringBuilder sb = StringBuilderCache.Acquire();
- byte b0, b1, b2, b3, b4;
- int l, i;
-
- l = buff.Length;
- i = 0;
-
- // Create l chars using the last 5 bits of each byte.
- // Consume 3 MSB bits 5 bytes at a time.
-
- do
- {
- b0 = (i < l) ? buff[i++] : (byte)0;
- b1 = (i < l) ? buff[i++] : (byte)0;
- b2 = (i < l) ? buff[i++] : (byte)0;
- b3 = (i < l) ? buff[i++] : (byte)0;
- b4 = (i < l) ? buff[i++] : (byte)0;
-
- // Consume the 5 Least significant bits of each byte
- sb.Append(s_Base32Char[b0 & 0x1F]);
- sb.Append(s_Base32Char[b1 & 0x1F]);
- sb.Append(s_Base32Char[b2 & 0x1F]);
- sb.Append(s_Base32Char[b3 & 0x1F]);
- sb.Append(s_Base32Char[b4 & 0x1F]);
-
- // Consume 3 MSB of b0, b1, MSB bits 6, 7 of b3, b4
- sb.Append(s_Base32Char[(
- ((b0 & 0xE0) >> 5) |
- ((b3 & 0x60) >> 2))]);
-
- sb.Append(s_Base32Char[(
- ((b1 & 0xE0) >> 5) |
- ((b4 & 0x60) >> 2))]);
-
- // Consume 3 MSB bits of b2, 1 MSB bit of b3, b4
-
- b2 >>= 5;
-
- Contract.Assert(((b2 & 0xF8) == 0), "Unexpected set bits");
-
- if ((b3 & 0x80) != 0)
- b2 |= 0x08;
- if ((b4 & 0x80) != 0)
- b2 |= 0x10;
-
- sb.Append(s_Base32Char[b2]);
-
- } while (i < l);
-
- return StringBuilderCache.GetStringAndRelease(sb);
- }
-
- // ".." can only be used if it is specified as a part of a valid File/Directory name. We disallow
- // the user being able to use it to move up directories. Here are some examples eg
- // Valid: a..b abc..d
- // Invalid: ..ab ab.. .. abc..d\abc..
- //
- internal static void CheckSearchPattern(String searchPattern)
- {
- int index;
- while ((index = searchPattern.IndexOf("..", StringComparison.Ordinal)) != -1) {
-
- if (index + 2 == searchPattern.Length) // Terminal ".." . Files names cannot end in ".."
- throw new ArgumentException(Environment.GetResourceString("Arg_InvalidSearchPattern"));
-
- if ((searchPattern[index+2] == DirectorySeparatorChar)
- || (searchPattern[index+2] == AltDirectorySeparatorChar))
- throw new ArgumentException(Environment.GetResourceString("Arg_InvalidSearchPattern"));
-
- searchPattern = searchPattern.Substring(index + 2);
- }
-
- }
-
- internal static void CheckInvalidPathChars(String path, bool checkAdditional = false)
- {
- if (path == null)
- throw new ArgumentNullException("path");
-
- if (PathInternal.HasIllegalCharacters(path, checkAdditional))
- throw new ArgumentException(Environment.GetResourceString("Argument_InvalidPathChars"));
- }
-
- internal static String InternalCombine(String path1, String path2) {
- if (path1==null || path2==null)
- throw new ArgumentNullException((path1==null) ? "path1" : "path2");
- Contract.EndContractBlock();
- CheckInvalidPathChars(path1);
- CheckInvalidPathChars(path2);
-
- if (path2.Length == 0)
- throw new ArgumentException(Environment.GetResourceString("Argument_PathEmpty"), "path2");
- if (IsPathRooted(path2))
- throw new ArgumentException(Environment.GetResourceString("Arg_Path2IsRooted"), "path2");
- int i = path1.Length;
- if (i == 0) return path2;
- char ch = path1[i - 1];
- if (ch != DirectorySeparatorChar && ch != AltDirectorySeparatorChar && ch != VolumeSeparatorChar)
- return path1 + DirectorySeparatorCharAsString + path2;
- return path1 + path2;
- }
-
- }
-}