Path.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ FXUpdate3074 / FXUpdate3074 / 1.1 / untmp / whidbey / QFE / ndp / clr / src / BCL / System / IO / Path.cs / 4 / Path.cs

                            // ==++== 
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--== 
/*============================================================
** 
** Class:  Path 
**
** 
** 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;
using System.Security.Cryptography;
using System.Runtime.CompilerServices; 
using System.Globalization;
using System.Runtime.Versioning; 
 
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.
    // 
    // File names cannot contain backslash (\), slash (/), colon (:),
    // asterick (*), question mark (?), quote ("), less than (<;), 
    // greater than (>;), or pipe (|).  The first three are used as directory 
    // separators on various platforms.  Asterick and question mark are treated
    // as wild cards.  Less than, Greater than, and pipe all redirect input 
    // or output from a program to a file or some combination thereof.  Quotes
    // are special.
    //
    // We are guaranteeing that Path.SeparatorChar is the correct 
    // directory separator on all platforms, and we will support
    // Path.AltSeparatorChar as well.  To write cross platform 
    // code with minimal pain, you can use slash (/) as a directory separator in 
    // your strings.
     // Class contains only static data, no need to serialize 
    [ComVisible(true)]
    public static class Path
    {
        // Platform specific directory separator character.  This is backslash 
        // ('\') on Windows, slash ('/') on Unix, and colon (':') on Mac.
        // 
#if !PLATFORM_UNIX 
        public static readonly char DirectorySeparatorChar = '\\';
#else 
        public static readonly char DirectorySeparatorChar = '/';
#endif // !PLATFORM_UNIX

        // Platform specific alternate directory separator character. 
        // This is backslash ('\') on Unix, and slash ('/') on Windows
        // and MacOS. 
        // 
#if !PLATFORM_UNIX
        public static readonly char AltDirectorySeparatorChar = '/'; 
#else
        public static readonly char AltDirectorySeparatorChar = '\\';
#endif // !PLATFORM_UNIX
 
        // 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.")]
        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 };
 

        private static readonly char[] RealInvalidPathChars = { '\"', '<', '>', '|', '\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 }; 
 
        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, ':', '*', '?', '\\', '/' };
 
#if !PLATFORM_UNIX
        public static readonly char PathSeparator = ';';
#else
        public static readonly char PathSeparator = ':'; 
#endif // !PLATFORM_UNIX
 
        // Make this public sometime. 
        // MaxPath accounts for the null-terminating character, for example, the maximum path on the D drive is "D:\<256 chars>\0".
        internal static readonly int MaxPath = 260; 

        // 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) {
            if (path != null) {
                CheckInvalidPathChars(path);
                path = FixupPath(path); 
                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);
 
            int i = 0;
            int length = path.Length; 
 
#if !PLATFORM_UNIX
            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 
            if (length >= 1 && (IsDirectorySeparator(path[0]))) { 
                i = 1;
            } 
            return i;
#endif // !PLATFORM_UNIX
        }
 
        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.
        // 
        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. 
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)] 
        public static String GetFullPath(String path) { 
            String fullPath = GetFullPathInternal(path);
            new FileIOPermission( FileIOPermissionAccess.PathDiscovery, new String[] { fullPath }, false, false ).Demand(); 
            return fullPath;
        }

        // This method is package access to let us quickly get a string name 
        // while avoiding a security
 
 

 

        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        internal static String GetFullPathInternal(String path) { 
            if (path == null)
                throw new ArgumentNullException("path"); 
 
            String newPath = NormalizePath(path, true);
 
            return newPath;
        }

        [ResourceExposure(ResourceScope.Machine)] 
        [ResourceConsumption(ResourceScope.Machine)]
        internal static String NormalizePath(String path, bool fullCheck) { 
#if !FEATURE_PAL 
            // P/Invoke doesn't support char* marshalling on Win9x.
            // So use the slower code path on 9x and faster stackalloc based 
            // implementation in NT. This is not an ideal situation, the code
            // is duplicated in 2 places, so some amount of maintenance involved
            // but this is only a temp solution until we get rid off 9x support.
 
            //
 
 

 


            if (Environment.nativeIsWin9x()) {
                return NormalizePathSlow(path, fullCheck); 
            }
#endif 
            return NormalizePathFast(path, fullCheck); 
        }
 
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        internal static String NormalizePathSlow(String path, bool fullCheck) {
            BCLDebug.Assert(path != null, "path can't be null"); 
            // If we're doing a full path
 
 
            if (fullCheck) {
                // Trim whitespace off the end of the string. 
                path = path.TrimEnd();

                // Look for illegal path characters.
                CheckInvalidPathChars(path); 
            }
 
            int index = 0; 
            char[] newBuffer = new char[MaxPath];
            int newBufferIndex = 0; 
            char[] finalBuffer = null;
            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;
            bool mightBeShortFileName = false; 

#if !PLATFORM_UNIX 
            // Win9x fixup - //server/share becomes c://server/share. 
            // This prevents our code from turning "\\server" into "\server".
            // On Win9x, //server/share becomes c://server/share 
            if (path.Length > 0 && (path[0] == DirectorySeparatorChar || path[0] == AltDirectorySeparatorChar)) {
                newBuffer[newBufferIndex++] = '\\';
                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 (newBufferIndex + numDots + 1 >= MaxPath)
                                throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); 

                            if (numDots == 2) { 
                                newBuffer[newBufferIndex++] = '.'; 
                            }
 
                            newBuffer[newBufferIndex++] = '.';
                            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[newBufferIndex++] = DirectorySeparatorChar;
                            } 
                        }
                    } 
                    numDots = 0; 
                    numSpaces = 0;  // Suppress trailing spaces
 
                    if (!fixupDirectorySeparator) {
                        fixupDirectorySeparator = true;

                        if (newBufferIndex + 1 >= MaxPath) 
                            throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong"));
 
                        newBuffer[newBufferIndex++] = 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 users 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:\DO----E~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[newBufferIndex] = '\0';
                        TryExpandShortFileName(newBuffer, ref newBufferIndex, MAX_PATH); 
                        mightBeShortFileName = false; 
                    }
#endif 
                } // 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 == '~') 
                        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 
                            uint spaceCount = 0; // How many spaces did we write out, numSpaces has already been reset.
                            while((spaceCount < newBufferIndex) && newBuffer[spaceCount] == ' ') 
                                spaceCount++;
                            if (numSigChars - spaceCount == 1) {
                                newBuffer[0] = driveLetter; // Overwrite spaces, we need a special case to not break "  foo" as a relative path.
                                newBufferIndex=1; 
                            }
                        } 
                        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) {
                            path.CopyTo(lastSigChar + 1, newBuffer, newBufferIndex, numCharsToCopy);
                            newBufferIndex += numCharsToCopy; 
                        }
                        numDots = 0; 
                        numSpaces = 0; 
                    }
 
                    if (newBufferIndex + 1 >= MaxPath)
                        throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong"));

                    newBuffer[newBufferIndex++] = currentChar; 
                    lastSigChar = index;
                } 
 
                index++;
            } // end while 

            // 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 (newBufferIndex + numDots >= MaxPath) 
                        throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong"));

                    if (numDots == 2) {
                        newBuffer[newBufferIndex++] = '.'; 
                    }
 
                    newBuffer[newBufferIndex++] = '.'; 
                }
            } // if (numSigChars == 0) 

            // If we ended up eating all the characters, bail out.
            if (newBufferIndex == 0)
                throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal")); 

            BCLDebug.Assert(newBufferIndex <= MaxPath, "Overflowed temporary path buffer" ); 
            newBuffer[newBufferIndex] = '\0'; 

            // 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 
            if (fullCheck &&
                (CharArrayStartsWithOrdinal(newBuffer, newBufferIndex, "http:", false) || 
                 CharArrayStartsWithOrdinal(newBuffer, newBufferIndex, "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) { 
                TryExpandShortFileName(newBuffer, ref newBufferIndex, MaxPath);
            } 
#endif 

            // Call the Win32 API to do the final canonicalization step. 
            int result = 1;
            char[] pFinal;
            int len;
 
            if (fullCheck) {
                // 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 > 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 

 
 
                // 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


 

                finalBuffer = new char[MaxPath + 1]; 
                result = Win32Native.GetFullPathName(newBuffer, MaxPath + 1, finalBuffer, IntPtr.Zero); 

                // If success, the return buffer length does not account for the terminating null character. 
                // If failure, the return buffer length does account for the path + the terminating null character.
                if (result > MaxPath) {
                    finalBuffer = new char[result];
                    result = Win32Native.GetFullPathName(newBuffer, result, finalBuffer, IntPtr.Zero); 

                    // Fullpath is genuinely long 
                    if (result > MaxPath) 
                        throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong"));
                } 

                BCLDebug.Assert(result <= MaxPath, "did we accidently remove a PathTooLongException check?");
                if (result == 0 && newBuffer[0] != '\0') {
                    __Error.WinIOError(); 
                }
                else if (result < MaxPath)  // May be necessary for empty strings 
                    finalBuffer[result] = '\0'; 
#if !FEATURE_PAL
                // GetFullPathName on Win9x returns the length in TCHARS 
                if (Environment.nativeIsWin9x()) {
                    for (int i=0; i= 0) { 
                            BCLDebug.Assert(lastSlash < len, "path unexpectedly ended in a '\'");
                            char[] savedName = new char[len - lastSlash - 1]; 
                            Array.Copy(finalBuffer, lastSlash + 1, savedName, 0, len - lastSlash - 1); 
                            finalBuffer[lastSlash] = '\0';
                            r = TryExpandShortFileName(finalBuffer, ref lastSlash, MaxPath); 

                            // Clean up changes made to the finalBuffer.
                            finalBuffer[lastSlash] = DirectorySeparatorChar;
 
                            Array.Copy(savedName, 0, finalBuffer, lastSlash + 1, savedName.Length);
                            if (r) 
                                len = lastSlash + 1 + savedName.Length; 
                        }
                    } 
                }
#endif
            }
            else { 
                pFinal = newBuffer;
                len = newBufferIndex; 
            } 

            if (result != 0) { 
                /* Throw an ArgumentException for paths like \\, \\server, \\server\
                   This

*/ 
                if (pFinal[0] == '\\' && pFinal[1] == '\\') {
                    int startIndex = 2; 
                    while (startIndex < result) { 
                        if (pFinal[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 (CharArrayStartsWithOrdinal(pFinal, len, "\\\\?\\globalroot", true)) 
                        throw new ArgumentException(Environment.GetResourceString("Arg_PathGlobalRoot")); 
                }
            } 

            // Check our result and form the managed string as necessary.

            if (len >= MaxPath) 
                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 new String(pFinal, 0, len); 
        } 

        private static bool CharArrayStartsWithOrdinal(char[] array, int numChars, String compareTo, bool ignoreCase) 
        {
            if (numChars < compareTo.Length)
                return false;
 
            if (ignoreCase) {
                String s = new String(array, 0, compareTo.Length); 
                return compareTo.Equals(s, StringComparison.OrdinalIgnoreCase); 
            }
            else { 
                for(int i=0; i= MaxPath)
                throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); 
            if (r == 0) {
                // Note: GetLongPathName will return ERROR_INVALID_FUNCTION on a 
                // path like \\.\PHYSICALDEVICE0 - some device driver doesn't 
                // support GetLongPathName on that string.  This behavior is
                // by design, according to the Core File Services team. 
                // We also get ERROR_NOT_ENOUGH_QUOTA in SQL_CLR_STRESS runs
                // intermittently on paths like D:\DO----E~1\user\LOCALS~1\Temp\
                return false;
            } 

#if !FEATURE_PAL 
            // GetLongPathName on Win9x returns the length in TCHARS 
            if (Environment.nativeIsWin9x()) {
                for (int i=0; i= MaxPath) 
                throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong"));

            buffer[index] = value;
        } 

        [ResourceExposure(ResourceScope.Machine)] 
        [ResourceConsumption(ResourceScope.Machine)] 
        // This version allocates the path buffer on the stack instead of heap for perf
        internal unsafe static String NormalizePathFast(String path, bool fullCheck) { 
            BCLDebug.Assert(path != null, "path can't be null");
            // If we're doing a full path

 
            // Win9x: to fixup path to replace multiple slashes with a single slash
            if (fullCheck) { 
                // Trim whitespace off the end of the string. 
                path = path.TrimEnd();
 
                // Look for illegal path characters.
                CheckInvalidPathChars(path);
            }
 
            int index = 0;
            // Allocating this on the stack instead of heap for workingset/perf gain. 
            // We need to be careful when we are indexing on the pointer as the operations 
            // are unsafe and not protected by bounds
            char *newBuffer = stackalloc char[MaxPath]; 
            int newBufferIndex = 0;
            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;
            bool mightBeShortFileName = false; 

#if !PLATFORM_UNIX 
            // Win9x fixup - //server/share becomes c://server/share. 
            // This prevents our code from turning "\\server" into "\server".
            // On Win9x, //server/share becomes c://server/share 
            if (path.Length > 0 && (path[0] == DirectorySeparatorChar || path[0] == AltDirectorySeparatorChar)) {
                SafeSetStackPointerValue(newBuffer, newBufferIndex++, '\\');
                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) {
                                SafeSetStackPointerValue(newBuffer, newBufferIndex++, '.'); 
                            }
 
                            SafeSetStackPointerValue(newBuffer, newBufferIndex++, '.'); 
                            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)) 
                            {
                                SafeSetStackPointerValue(newBuffer, newBufferIndex++, DirectorySeparatorChar); 
                            }
                        }
                    }
                    numDots = 0; 
                    numSpaces = 0;  // Suppress trailing spaces
 
                    if (!fixupDirectorySeparator) { 
                        fixupDirectorySeparator = true;
                        SafeSetStackPointerValue(newBuffer, newBufferIndex++, 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:\DO----E~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) { 
                        SafeSetStackPointerValue(newBuffer, newBufferIndex, '\0'); 
                        TryExpandShortFileName(newBuffer, ref newBufferIndex, MAX_PATH);
                        mightBeShortFileName = false; 
                    }
#endif
                } // 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 == '~') 
                        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 
                            uint spaceCount = 0; // How many spaces did we write out, numSpaces has already been reset.
                            while((spaceCount < newBufferIndex) && newBuffer[spaceCount] == ' ')
                                spaceCount++;
                            if (numSigChars - spaceCount == 1) { 
                                //Safe to update stack ptr directly
                                newBuffer[0] = driveLetter; // Overwrite spaces, we need a special case to not break "  foo" as a relative path. 
                                newBufferIndex=1; 
                            }
                        } 
                        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 "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) {
                        SafeSetStackPointerValue(newBuffer, newBufferIndex++, '.');
                    }
 
                    SafeSetStackPointerValue(newBuffer, newBufferIndex++, '.');
                } 
            } // if (numSigChars == 0) 

            // If we ended up eating all the characters, bail out. 
            if (newBufferIndex == 0)
                throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal"));

            // Null terminate the buffer. We will allow at most a path of length MaxPath-1 (accounting for the null terminating char). 
            SafeSetStackPointerValue(newBuffer, newBufferIndex, '\0');
 
            // 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
            if (fullCheck &&
                (CharArrayStartsWithOrdinal(newBuffer, newBufferIndex, "http:", false) || 
                 CharArrayStartsWithOrdinal(newBuffer, newBufferIndex, "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) {
                TryExpandShortFileName(newBuffer, ref newBufferIndex, MaxPath);
            } 
#endif
 
            // Call the Win32 API to do the final canonicalization step. 
            int result = 1;
            char *pFinal; 
            int len;

            if (fullCheck) {
                // 

 
 

 



 

 
                // 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 



 

                char *finalBuffer = stackalloc char[MaxPath + 1]; 
                result = Win32Native.GetFullPathName(newBuffer, MaxPath + 1, finalBuffer, IntPtr.Zero); 

                // If success, the return buffer length does not account for the terminating null character. 
                // If in-sufficient buffer, the return buffer length does account for the path + the terminating null character.
                // If failure, the return buffer length is zero
                if (result > MaxPath) {
                    char *tempBuffer = stackalloc char[result]; 
                    finalBuffer = tempBuffer;
                    result = Win32Native.GetFullPathName(newBuffer, result, finalBuffer, IntPtr.Zero); 
                } 

                // Fullpath is genuinely long 
                if (result >= MaxPath)
                    throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong"));

                BCLDebug.Assert(result < MaxPath, "did we accidently remove a PathTooLongException check?"); 
                if (result == 0 && newBuffer[0] != '\0') {
                    __Error.WinIOError(); 
                } 
                else if (result < MaxPath) {
                    // Null terminate explicitly (may be only needed for some cases such as empty strings) 
                    // GetFullPathName return length doesn't account for null terminating char...
                    finalBuffer[result] = '\0'; // Safe to write directly as result is < MaxPath
                }
 
                pFinal = finalBuffer;
                // Doesn't account for null terminating char. Think of this as the last 
                // valid index into the buffer but not the length of the buffer 
                len = result;
 
#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,
                mightBeShortFileName = false; 
                for(uint i=0; i=0; i--) {
                            if (finalBuffer[i] == DirectorySeparatorChar) { 
                                lastSlash = i;
                                break; 
                            } 
                        }
 
                        if (lastSlash >= 0) {

                            // This bounds
                            if (len >= MaxPath) 
                                throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong"));
 
                            int lenSavedName = len - lastSlash - 1; 
                            BCLDebug.Assert(lastSlash < len, "path unexpectedly ended in a '\'");
                            char* savedName = stackalloc char[lenSavedName]; 

                            Buffer.memcpy(finalBuffer, lastSlash + 1, savedName, 0, lenSavedName);

                            SafeSetStackPointerValue(finalBuffer, lastSlash, '\0'); 
                            r = TryExpandShortFileName(finalBuffer, ref lastSlash, MaxPath);
 
                            // Clean up changes made to the finalBuffer. 
                            SafeSetStackPointerValue(finalBuffer, lastSlash, DirectorySeparatorChar);
 
                            if (lastSlash + 1 + lenSavedName >= MaxPath)
                                throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong"));

                            Buffer.memcpy(savedName, 0, finalBuffer, lastSlash + 1, lenSavedName); 

                            if (r) 
                                len = lastSlash + 1 + lenSavedName; 
                        }
                    } 
                }
#endif
            }
            else { 
                pFinal = newBuffer;
                len = newBufferIndex; 
            } 

            if (result != 0) { 
                /* Throw an ArgumentException for paths like \\, \\server, \\server\
                   This

*/ 
                if (pFinal[0] == '\\' && pFinal[1] == '\\') {
                    int startIndex = 2; 
                    while (startIndex < result) { 
                        if (pFinal[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 (CharArrayStartsWithOrdinal(pFinal, len, "\\\\?\\globalroot", true)) 
                        throw new ArgumentException(Environment.GetResourceString("Arg_PathGlobalRoot")); 
                }
            } 

            // Check our result and form the managed string as necessary.
            if (len >= MaxPath)
                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 new String(pFinal, 0, len);
        } 
 
        private static unsafe bool CharArrayStartsWithOrdinal(char* array, int numChars, String compareTo, bool ignoreCase)
        { 
            if (numChars < compareTo.Length)
                return false;
            if (ignoreCase) {
                String s = new String(array, 0, compareTo.Length); 
                return compareTo.Equals(s, StringComparison.OrdinalIgnoreCase);
            } 
            else { 
                for(int i=0; i= MaxPath)
                throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); 
            if (r == 0) {
                // Note: GetLongPathName will return ERROR_INVALID_FUNCTION on a 
                // path like \\.\PHYSICALDEVICE0 - some device driver doesn't 
                // support GetLongPathName on that string.  This behavior is
                // by design, according to the Core File Services team. 
                // We also get ERROR_NOT_ENOUGH_QUOTA in SQL_CLR_STRESS runs
                // intermittently on paths like D:\DO----E~1\user\LOCALS~1\Temp\
                return false;
            } 

            // Safe to copy as we have already done MaxPath bound checking 
            Buffer.memcpy(shortFileNameBuffer, 0, buffer, 0, r); 
            bufferLength = r;
            // We should explicitly null terminate as in some cases the long version of the path 
            // might actually be shorter than what we started with because of Win32's normailization
            // Safe to write directly as bufferLength is guaranteed to be < MaxPath
            buffer[bufferLength] = '\0';
            return true; 
        }
 
        // Win9x: to fixup path to replace multiple slashes with a single slash 
        // This is marked as exposing no resources because it calls
        // NormalizePath and passes in false for fullCheck.  This is mostly 
        // used to simply clean up the format of the string, but not to fully
        // qualify it.
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] 
        internal static String FixupPath(String path)
        { 
            String newPath = NormalizePath(path, false); 
            return newPath;
        } 

        // 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. 
        //
        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;
        } 

        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.
        //
        public static String GetPathRoot(String path) {
            if (path == null) return null; 
            path = FixupPath(path);
            return path.Substring(0, GetRootLength(path)); 
        } 

        [ResourceExposure(ResourceScope.Machine)] 
        [ResourceConsumption(ResourceScope.Machine)]
        public static String GetTempPath()
        {
            new EnvironmentPermission(PermissionState.Unrestricted).Demand(); 
            StringBuilder sb = new StringBuilder(MAX_PATH);
            uint r = Win32Native.GetTempPath(MAX_PATH, sb); 
            String path = sb.ToString(); 
            if (r==0) __Error.WinIOError();
            path = GetFullPathInternal(path); 
            return path;
        }

        // Returns a cryptographically strong random 8.3 string that can be 
        // used as either a folder name or a file name.
        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];
            RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
            rng.GetBytes(key);
            // rndCharArray is expected to be 16 chars 
            char[] rndCharArray = System.IO.IsolatedStorage.IsolatedStorage.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.
        [ResourceExposure(ResourceScope.AppDomain)]
        [ResourceConsumption(ResourceScope.Machine)] 
        public static String GetTempFileName()
        { 
            String path = GetTempPath(); 

            // Since this can write to the temp directory and theoretically 
            // cause a denial of service attack, demand FileIOPermission to
            // that directory.
            new FileIOPermission(FileIOPermissionAccess.Write, path).Demand();
 
            StringBuilder sb = new StringBuilder(MAX_PATH);
            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.
        // 
        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 (":"). 
        //
        public static bool IsPathRooted(String path) {
            if (path != null) {
                CheckInvalidPathChars(path); 

                int length = path.Length; 
                if ((length >= 1 && (path[0] == DirectorySeparatorChar || path[0] == AltDirectorySeparatorChar)) 
#if !PLATFORM_UNIX
                    || (length >= 2 && path[1] == VolumeSeparatorChar) 
#endif
                    ) return true;
            }
            return false; 
        }
 
        public static String Combine(String path1, String path2) { 
            if (path1==null || path2==null)
                throw new ArgumentNullException((path1==null) ? "path1" : "path2"); 
            CheckInvalidPathChars(path1);
            CheckInvalidPathChars(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 + DirectorySeparatorChar + path2; 
            return path1 + path2; 
        }
 

        // ".." 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; 
#if !FEATURE_PAL
            // Only

            if ((Environment.OSInfo & Environment.OSName.Win9x) != 0 && CanPathCircumventSecurityNative(searchPattern)) 
                throw new ArgumentException(Environment.GetResourceString("Arg_InvalidSearchPattern"));
#endif  // !FEATURE_PAL 
            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)
        { 
#if PLATFORM_UNIX
            if( path.StartsWith("\\\\")) 
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidPathChars")); 
#endif // PLATFORM_UNIX
 
            for (int i = 0; i < path.Length; i++)
            {
                int c = path[i];
 
                // Note: This list is duplicated in static char[] InvalidPathChars
                if (c == '\"' || c == '<' || c == '>' || c == '|' || c < 32) 
                    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");
            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 + DirectorySeparatorChar + path2;
            return path1 + path2; 
        }

#if !FEATURE_PAL
        // Checks for Unicode characters that map to potentially dangerous 
        // characters in the system's default ANSI code page.
        [MethodImplAttribute(MethodImplOptions.InternalCall)] 
        private static extern bool CanPathCircumventSecurityNative(String partOfPath); 
#endif  // !FEATURE_PAL
 
        // 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
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK