summaryrefslogtreecommitdiff
path: root/src/inc/clr/fs/path.h
blob: 7c1995ecb2f91bf66579dcf5205e80c349a4052e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// 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.
//

//
// This header provides general path-related file system services.

#ifndef _clr_fs_Path_h_
#define _clr_fs_Path_h_

#include "clrtypes.h"
#include "cor.h" // SELECTANY

#include "strsafe.h"

#include "clr/str.h"

#ifndef LONG_FORMAT_PATH_PREFIX
    #define LONG_FORMAT_PATH_PREFIX W("\\\\?\\")
#endif

namespace clr
{
    namespace fs
    {
        // This list taken from ndp/clr/src/bcl/system/io/path.cs
        SELECTANY WCHAR const g_rgInvalidPathChars[] =
            { W('"'), W('<'), W('>'), W('|'), W('\0'), (WCHAR)1, (WCHAR)2, (WCHAR)3, (WCHAR)4, (WCHAR)5, (WCHAR)6,
              (WCHAR)7, (WCHAR)8, (WCHAR)9, (WCHAR)10, (WCHAR)11, (WCHAR)12, (WCHAR)13, (WCHAR)14,
              (WCHAR)15, (WCHAR)16, (WCHAR)17, (WCHAR)18, (WCHAR)19, (WCHAR)20, (WCHAR)21, (WCHAR)22,
              (WCHAR)23, (WCHAR)24, (WCHAR)25, (WCHAR)26, (WCHAR)27, (WCHAR)28, (WCHAR)29, (WCHAR)30,
              (WCHAR)31 };

        class Path
        {
        public:
            //-----------------------------------------------------------------------------------------
            static inline bool
            Exists(
                LPCWSTR wzPath)
            {
                DWORD attrs = WszGetFileAttributes(wzPath);
                return (attrs != INVALID_FILE_ATTRIBUTES);
            }

            //-----------------------------------------------------------------------------------------
            // Returns true if wzPath represents a long format path (i.e., prefixed with '\\?\').
            static inline bool
            HasLongFormatPrefix(LPCWSTR wzPath)
            {
                _ASSERTE(!clr::str::IsNullOrEmpty(wzPath)); // Must check this first.
                return wcscmp(wzPath, LONG_FORMAT_PATH_PREFIX) == 0;
            }

            //-----------------------------------------------------------------------------------------
            // Returns true if wzPath represents a relative path.
            static inline bool
            IsRelative(LPCWSTR wzPath)
            {
                _ASSERTE(wzPath != nullptr);

                // Similar to System.IO.Path.IsRelative()
#if PLATFORM_UNIX
                if(wzPath[0] == VOLUME_SEPARATOR_CHAR_W)
                {
                    return false;
                }
#else
                // Check for a paths like "C:\..." or "\\...". Additional notes:
                // - "\\?\..." - long format paths are considered as absolute paths due to the "\\" prefix
                // - "\..." - these paths are relative, as they depend on the current drive
                // - "C:..." and not "C:\..." - these paths are relative, as they depend on the current directory for drive C
                if (wzPath[0] != W('\0') &&
                    wzPath[1] == VOLUME_SEPARATOR_CHAR_W &&
                    wzPath[2] == DIRECTORY_SEPARATOR_CHAR_W &&
                    (
                        (wzPath[0] >= W('A') && wzPath[0] <= W('Z')) ||
                        (wzPath[0] >= W('a') && wzPath[0] <= W('z'))
                    ))
                {
                    return false;
                }
                if (wzPath[0] == DIRECTORY_SEPARATOR_CHAR_W && wzPath[1] == DIRECTORY_SEPARATOR_CHAR_W)
                {
                    return false;
                }
#endif

                return true;
            }

            //-----------------------------------------------------------------------------------------
            // Combines two path parts. wzPathLeft must be a directory path and may be either absolute
            // or relative. wzPathRight may be a directory or file path and must be relative. The
            // result is placed in wzBuffer and the number of chars written is placed in pcchBuffer on
            // success; otherwise an error HRESULT is returned.
            static HRESULT
            Combine(LPCWSTR wzPathLeft, LPCWSTR wzPathRight, __in DWORD *pcchBuffer, __out_ecount(*pcchBuffer) LPWSTR wzBuffer)
            {
                STATIC_CONTRACT_NOTHROW;

                HRESULT hr = S_OK;

                if (clr::str::IsNullOrEmpty(wzPathLeft) || clr::str::IsNullOrEmpty(wzPathRight) || pcchBuffer == nullptr)
                    return E_INVALIDARG;

                LPWSTR wzBuf = wzBuffer;
                size_t cchBuf = *pcchBuffer;

                IfFailRet(StringCchCopyExW(wzBuf, cchBuf, wzPathLeft, &wzBuf, &cchBuf, STRSAFE_NULL_ON_FAILURE));
                IfFailRet(StringCchCatExW(wzBuf, cchBuf, wzBuf[-1] == DIRECTORY_SEPARATOR_CHAR_W ? W("") : DIRECTORY_SEPARATOR_STR_W, &wzBuf, &cchBuf, STRSAFE_NULL_ON_FAILURE));
                IfFailRet(StringCchCatExW(wzBuf, cchBuf, wzPathRight, &wzBuf, &cchBuf, STRSAFE_NULL_ON_FAILURE));

                return S_OK;
            }

            //-----------------------------------------------------------------------------------------
            // Checks if the path provided is valid within the specified constraints.
            // ***NOTE: does not yet check for invalid path characters.
            static bool
            IsValid(LPCWSTR wzPath, DWORD cchPath, bool fAllowLongFormat)
            {
                if (clr::str::IsNullOrEmpty(wzPath))
                    return false;

                bool fIsLongFormat = HasLongFormatPrefix(wzPath);

                if (fIsLongFormat && !fAllowLongFormat)
                    return false;

                if (!fIsLongFormat && cchPath > _MAX_PATH)
                    return false;

                return true;
            }
        };
    }
}

#endif // _clr_fs_Path_h_