diff options
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil')
-rw-r--r-- | src/libs/dutil/WixToolset.DUtil/inc/pathutil.h | 56 | ||||
-rw-r--r-- | src/libs/dutil/WixToolset.DUtil/path2utl.cpp | 149 | ||||
-rw-r--r-- | src/libs/dutil/WixToolset.DUtil/pathutil.cpp | 171 |
3 files changed, 307 insertions, 69 deletions
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h b/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h index 602b4a80..fc6bb3bb 100644 --- a/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h +++ b/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h | |||
@@ -6,7 +6,17 @@ | |||
6 | extern "C" { | 6 | extern "C" { |
7 | #endif | 7 | #endif |
8 | 8 | ||
9 | typedef enum PATH_EXPAND | 9 | typedef enum _PATH_CANONICALIZE |
10 | { | ||
11 | // Always prefix fully qualified paths with the long path prefix (\\?\). | ||
12 | PATH_CANONICALIZE_APPEND_LONG_PATH_PREFIX = 0x0001, | ||
13 | // Always terminate the path with \. | ||
14 | PATH_CANONICALIZE_BACKSLASH_TERMINATE = 0x0002, | ||
15 | // Don't collapse . or .. in the \\server\share portion of a UNC path. | ||
16 | PATH_CANONICALIZE_KEEP_UNC_ROOT = 0x0004, | ||
17 | } PATH_CANONICALIZE; | ||
18 | |||
19 | typedef enum _PATH_EXPAND | ||
10 | { | 20 | { |
11 | PATH_EXPAND_ENVIRONMENT = 0x0001, | 21 | PATH_EXPAND_ENVIRONMENT = 0x0001, |
12 | PATH_EXPAND_FULLPATH = 0x0002, | 22 | PATH_EXPAND_FULLPATH = 0x0002, |
@@ -29,7 +39,9 @@ DAPI_(LPCWSTR) PathExtension( | |||
29 | ); | 39 | ); |
30 | 40 | ||
31 | /******************************************************************* | 41 | /******************************************************************* |
32 | PathGetDirectory - extracts the directory from a path. | 42 | PathGetDirectory - extracts the directory from a path including the directory separator. |
43 | This means calling the function again with the previous result returns the same result. | ||
44 | Returns S_FALSE if the path only contains a file name. | ||
33 | ********************************************************************/ | 45 | ********************************************************************/ |
34 | DAPI_(HRESULT) PathGetDirectory( | 46 | DAPI_(HRESULT) PathGetDirectory( |
35 | __in_z LPCWSTR wzPath, | 47 | __in_z LPCWSTR wzPath, |
@@ -38,6 +50,7 @@ DAPI_(HRESULT) PathGetDirectory( | |||
38 | 50 | ||
39 | /******************************************************************* | 51 | /******************************************************************* |
40 | PathGetParentPath - extracts the parent directory from a full path. | 52 | PathGetParentPath - extracts the parent directory from a full path. |
53 | *psczDirectory is NULL if the path only contains a file name. | ||
41 | ********************************************************************/ | 54 | ********************************************************************/ |
42 | DAPI_(HRESULT) PathGetParentPath( | 55 | DAPI_(HRESULT) PathGetParentPath( |
43 | __in_z LPCWSTR wzPath, | 56 | __in_z LPCWSTR wzPath, |
@@ -63,6 +76,21 @@ DAPI_(HRESULT) PathPrefix( | |||
63 | ); | 76 | ); |
64 | 77 | ||
65 | /******************************************************************* | 78 | /******************************************************************* |
79 | PathFixedNormalizeSlashes - replaces all / with \ and | ||
80 | removes redundant consecutive slashes. | ||
81 | ********************************************************************/ | ||
82 | DAPI_(HRESULT) PathFixedNormalizeSlashes( | ||
83 | __inout_z LPWSTR wzPath | ||
84 | ); | ||
85 | |||
86 | /******************************************************************* | ||
87 | PathFixedReplaceForwardSlashes - replaces all / with \ | ||
88 | ********************************************************************/ | ||
89 | DAPI_(void) PathFixedReplaceForwardSlashes( | ||
90 | __inout_z LPWSTR wzPath | ||
91 | ); | ||
92 | |||
93 | /******************************************************************* | ||
66 | PathFixedBackslashTerminate - appends a \ if path does not have it | 94 | PathFixedBackslashTerminate - appends a \ if path does not have it |
67 | already, but fails if the buffer is | 95 | already, but fails if the buffer is |
68 | insufficient. | 96 | insufficient. |
@@ -77,7 +105,7 @@ DAPI_(HRESULT) PathFixedBackslashTerminate( | |||
77 | already. | 105 | already. |
78 | ********************************************************************/ | 106 | ********************************************************************/ |
79 | DAPI_(HRESULT) PathBackslashTerminate( | 107 | DAPI_(HRESULT) PathBackslashTerminate( |
80 | __inout LPWSTR* psczPath | 108 | __inout_z LPWSTR* psczPath |
81 | ); | 109 | ); |
82 | 110 | ||
83 | /******************************************************************* | 111 | /******************************************************************* |
@@ -168,7 +196,7 @@ DAPI_(HRESULT) PathGetKnownFolder( | |||
168 | /******************************************************************* | 196 | /******************************************************************* |
169 | PathIsFullyQualified - returns true if the path is fully qualified; false otherwise. | 197 | PathIsFullyQualified - returns true if the path is fully qualified; false otherwise. |
170 | Note that some rooted paths like C:dir are not fully qualified. | 198 | Note that some rooted paths like C:dir are not fully qualified. |
171 | For example, these are all fully qualified: C:\dir, \\server\share, \\?\C:\dir. | 199 | For example, these are all fully qualified: C:\dir, C:/dir, \\server\share, \\?\C:\dir. |
172 | For example, these are not fully qualified: C:dir, C:, \dir, dir, dir\subdir. | 200 | For example, these are not fully qualified: C:dir, C:, \dir, dir, dir\subdir. |
173 | *******************************************************************/ | 201 | *******************************************************************/ |
174 | DAPI_(BOOL) PathIsFullyQualified( | 202 | DAPI_(BOOL) PathIsFullyQualified( |
@@ -179,7 +207,7 @@ DAPI_(BOOL) PathIsFullyQualified( | |||
179 | /******************************************************************* | 207 | /******************************************************************* |
180 | PathIsRooted - returns true if the path is rooted; false otherwise. | 208 | PathIsRooted - returns true if the path is rooted; false otherwise. |
181 | Note that some rooted paths like C:dir are not fully qualified. | 209 | Note that some rooted paths like C:dir are not fully qualified. |
182 | For example, these are all rooted: C:\dir, C:dir, C:, \dir, \\server\share, \\?\C:\dir. | 210 | For example, these are all rooted: C:\dir, C:/dir, C:dir, C:, \dir, \\server\share, \\?\C:\dir. |
183 | For example, these are not rooted: dir, dir\subdir. | 211 | For example, these are not rooted: dir, dir\subdir. |
184 | *******************************************************************/ | 212 | *******************************************************************/ |
185 | DAPI_(BOOL) PathIsRooted( | 213 | DAPI_(BOOL) PathIsRooted( |
@@ -240,7 +268,7 @@ DAPI_(HRESULT) PathGetHierarchyArray( | |||
240 | ); | 268 | ); |
241 | 269 | ||
242 | /******************************************************************* | 270 | /******************************************************************* |
243 | PathCanonicalizePath - wrapper around PathCanonicalizeW. | 271 | PathCanonicalizePath - wrapper around PathCanonicalizeW. |
244 | *******************************************************************/ | 272 | *******************************************************************/ |
245 | DAPI_(HRESULT) PathCanonicalizePath( | 273 | DAPI_(HRESULT) PathCanonicalizePath( |
246 | __in_z LPCWSTR wzPath, | 274 | __in_z LPCWSTR wzPath, |
@@ -248,8 +276,20 @@ DAPI_(HRESULT) PathCanonicalizePath( | |||
248 | ); | 276 | ); |
249 | 277 | ||
250 | /******************************************************************* | 278 | /******************************************************************* |
251 | PathDirectoryContainsPath - checks if wzPath is located inside | 279 | PathCanonicalizeForComparison - canonicalizes the path based on the given flags. |
252 | wzDirectory. | 280 | . and .. directories are collapsed. |
281 | All / are replaced with \. | ||
282 | All redundant consecutive slashes are replaced with a single \. | ||
283 | *******************************************************************/ | ||
284 | DAPI_(HRESULT) PathCanonicalizeForComparison( | ||
285 | __in_z LPCWSTR wzPath, | ||
286 | __in DWORD dwCanonicalizeFlags, | ||
287 | __deref_out_z LPWSTR* psczCanonicalized | ||
288 | ); | ||
289 | |||
290 | /******************************************************************* | ||
291 | PathDirectoryContainsPath - checks if wzPath is located inside wzDirectory. | ||
292 | wzDirectory must be a fully qualified path. | ||
253 | *******************************************************************/ | 293 | *******************************************************************/ |
254 | DAPI_(HRESULT) PathDirectoryContainsPath( | 294 | DAPI_(HRESULT) PathDirectoryContainsPath( |
255 | __in_z LPCWSTR wzDirectory, | 295 | __in_z LPCWSTR wzDirectory, |
diff --git a/src/libs/dutil/WixToolset.DUtil/path2utl.cpp b/src/libs/dutil/WixToolset.DUtil/path2utl.cpp index ff3a946d..45157d0b 100644 --- a/src/libs/dutil/WixToolset.DUtil/path2utl.cpp +++ b/src/libs/dutil/WixToolset.DUtil/path2utl.cpp | |||
@@ -9,6 +9,7 @@ | |||
9 | #define PathExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) | 9 | #define PathExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) |
10 | #define PathExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) | 10 | #define PathExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) |
11 | #define PathExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) | 11 | #define PathExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) |
12 | #define PathExitWithRootFailure(x, e, s, ...) ExitWithRootFailureSource(DUTIL_SOURCE_PATHUTIL, x, e, s, __VA_ARGS__) | ||
12 | #define PathExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) | 13 | #define PathExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__) |
13 | #define PathExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__) | 14 | #define PathExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__) |
14 | #define PathExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__) | 15 | #define PathExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__) |
@@ -42,63 +43,141 @@ LExit: | |||
42 | return hr; | 43 | return hr; |
43 | } | 44 | } |
44 | 45 | ||
45 | DAPI_(HRESULT) PathDirectoryContainsPath( | 46 | DAPI_(HRESULT) PathCanonicalizeForComparison( |
46 | __in_z LPCWSTR wzDirectory, | 47 | __in_z LPCWSTR wzPath, |
47 | __in_z LPCWSTR wzPath | 48 | __in DWORD dwCanonicalizeFlags, |
49 | __deref_out_z LPWSTR* psczCanonicalized | ||
48 | ) | 50 | ) |
49 | { | 51 | { |
50 | HRESULT hr = S_OK; | 52 | HRESULT hr = S_OK; |
51 | LPWSTR sczPath = NULL; | 53 | LPWSTR sczNormalizedPath = NULL; |
52 | LPWSTR sczDirectory = NULL; | 54 | LPCWSTR wzNormalizedPath = NULL; |
53 | LPWSTR sczOriginalPath = NULL; | 55 | SIZE_T cchUncRootLength = 0; |
54 | LPWSTR sczOriginalDirectory = NULL; | 56 | BOOL fHasPrefix = FALSE; |
55 | 57 | ||
56 | hr = PathCanonicalizePath(wzPath, &sczOriginalPath); | 58 | hr = StrAllocString(&sczNormalizedPath, wzPath, 0); |
57 | PathExitOnFailure(hr, "Failed to canonicalize the path."); | 59 | PathExitOnFailure(hr, "Failed to allocate string for the normalized path."); |
58 | 60 | ||
59 | hr = PathCanonicalizePath(wzDirectory, &sczOriginalDirectory); | 61 | PathFixedNormalizeSlashes(sczNormalizedPath); |
60 | PathExitOnFailure(hr, "Failed to canonicalize the directory."); | 62 | |
63 | wzNormalizedPath = sczNormalizedPath; | ||
61 | 64 | ||
62 | if (!sczOriginalPath || !*sczOriginalPath) | 65 | if (PATH_CANONICALIZE_KEEP_UNC_ROOT & dwCanonicalizeFlags) |
63 | { | 66 | { |
64 | ExitFunction1(hr = S_FALSE); | 67 | if (L'\\' == sczNormalizedPath[0] && (L'\\' == sczNormalizedPath[1] || L'?' == sczNormalizedPath[1]) && L'?' == sczNormalizedPath[2] && L'\\' == sczNormalizedPath[3]) |
68 | { | ||
69 | if (L'U' == sczNormalizedPath[4] && L'N' == sczNormalizedPath[5] && L'C' == sczNormalizedPath[6] && L'\\' == sczNormalizedPath[7]) | ||
70 | { | ||
71 | cchUncRootLength = 8; | ||
72 | } | ||
73 | } | ||
74 | else if (L'\\' == sczNormalizedPath[0] && L'\\' == sczNormalizedPath[1]) | ||
75 | { | ||
76 | cchUncRootLength = 2; | ||
77 | } | ||
78 | |||
79 | if (cchUncRootLength) | ||
80 | { | ||
81 | DWORD dwRemainingSlashes = 2; | ||
82 | |||
83 | for (wzNormalizedPath += cchUncRootLength; *wzNormalizedPath && dwRemainingSlashes; ++wzNormalizedPath) | ||
84 | { | ||
85 | ++cchUncRootLength; | ||
86 | |||
87 | if (L'\\' == *wzNormalizedPath) | ||
88 | { | ||
89 | --dwRemainingSlashes; | ||
90 | } | ||
91 | } | ||
92 | } | ||
93 | } | ||
94 | |||
95 | if (*wzNormalizedPath) | ||
96 | { | ||
97 | hr = PathCanonicalizePath(wzNormalizedPath, psczCanonicalized); | ||
98 | PathExitOnFailure(hr, "Failed to canonicalize: %ls", wzNormalizedPath); | ||
65 | } | 99 | } |
66 | if (!sczOriginalDirectory || !*sczOriginalDirectory) | 100 | else |
67 | { | 101 | { |
68 | ExitFunction1(hr = S_FALSE); | 102 | Assert(cchUncRootLength); |
103 | ReleaseStr(*psczCanonicalized); | ||
104 | *psczCanonicalized = sczNormalizedPath; | ||
105 | sczNormalizedPath = NULL; | ||
106 | cchUncRootLength = 0; | ||
69 | } | 107 | } |
70 | 108 | ||
71 | sczPath = sczOriginalPath; | 109 | if (cchUncRootLength) |
72 | sczDirectory = sczOriginalDirectory; | 110 | { |
111 | hr = StrAllocPrefix(psczCanonicalized, sczNormalizedPath, cchUncRootLength); | ||
112 | PathExitOnFailure(hr, "Failed to prefix the UNC root to the canonicalized path."); | ||
113 | } | ||
73 | 114 | ||
74 | for (; *sczDirectory;) | 115 | if (PATH_CANONICALIZE_BACKSLASH_TERMINATE & dwCanonicalizeFlags) |
75 | { | 116 | { |
76 | if (!*sczPath) | 117 | hr = PathBackslashTerminate(psczCanonicalized); |
77 | { | 118 | PathExitOnFailure(hr, "Failed to backslash terminate the canonicalized path"); |
78 | ExitFunction1(hr = S_FALSE); | 119 | } |
79 | } | ||
80 | 120 | ||
81 | if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczDirectory, 1, sczPath, 1)) | 121 | if ((PATH_CANONICALIZE_APPEND_LONG_PATH_PREFIX & dwCanonicalizeFlags) && |
82 | { | 122 | PathIsFullyQualified(*psczCanonicalized, &fHasPrefix) && !fHasPrefix) |
83 | ExitFunction1(hr = S_FALSE); | 123 | { |
84 | } | 124 | hr = PathPrefix(psczCanonicalized); |
125 | PathExitOnFailure(hr, "Failed to ensure the long path prefix on the canonicalized path"); | ||
126 | } | ||
127 | |||
128 | LExit: | ||
129 | ReleaseStr(sczNormalizedPath); | ||
85 | 130 | ||
86 | ++sczDirectory; | 131 | return hr; |
87 | ++sczPath; | 132 | } |
133 | |||
134 | DAPI_(HRESULT) PathDirectoryContainsPath( | ||
135 | __in_z LPCWSTR wzDirectory, | ||
136 | __in_z LPCWSTR wzPath | ||
137 | ) | ||
138 | { | ||
139 | HRESULT hr = S_OK; | ||
140 | LPWSTR sczCanonicalizedDirectory = NULL; | ||
141 | LPWSTR sczCanonicalizedPath = NULL; | ||
142 | DWORD dwDefaultFlags = PATH_CANONICALIZE_APPEND_LONG_PATH_PREFIX | PATH_CANONICALIZE_KEEP_UNC_ROOT; | ||
143 | size_t cchDirectory = 0; | ||
144 | |||
145 | if (!wzDirectory || !*wzDirectory) | ||
146 | { | ||
147 | PathExitWithRootFailure(hr, E_INVALIDARG, "wzDirectory is required."); | ||
88 | } | 148 | } |
149 | if (!wzPath || !*wzPath) | ||
150 | { | ||
151 | PathExitWithRootFailure(hr, E_INVALIDARG, "wzPath is required."); | ||
152 | } | ||
153 | |||
154 | hr = PathCanonicalizeForComparison(wzDirectory, dwDefaultFlags | PATH_CANONICALIZE_BACKSLASH_TERMINATE, &sczCanonicalizedDirectory); | ||
155 | PathExitOnFailure(hr, "Failed to canonicalize the directory."); | ||
89 | 156 | ||
90 | --sczDirectory; | 157 | hr = PathCanonicalizeForComparison(wzPath, dwDefaultFlags, &sczCanonicalizedPath); |
91 | if (('\\' == *sczDirectory && *sczPath) || '\\' == *sczPath) | 158 | PathExitOnFailure(hr, "Failed to canonicalize the path."); |
159 | |||
160 | if (!PathIsFullyQualified(sczCanonicalizedDirectory, NULL)) | ||
92 | { | 161 | { |
93 | hr = S_OK; | 162 | PathExitWithRootFailure(hr, E_INVALIDARG, "wzDirectory must be a fully qualified path."); |
94 | } | 163 | } |
95 | else | 164 | if (!sczCanonicalizedPath || !*sczCanonicalizedPath) |
96 | { | 165 | { |
97 | hr = S_FALSE; | 166 | ExitFunction1(hr = S_FALSE); |
98 | } | 167 | } |
99 | 168 | ||
169 | hr = ::StringCchLengthW(sczCanonicalizedDirectory, STRSAFE_MAX_CCH, &cchDirectory); | ||
170 | PathExitOnFailure(hr, "Failed to get length of canonicalized directory."); | ||
171 | |||
172 | if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczCanonicalizedDirectory, (DWORD)cchDirectory, sczCanonicalizedPath, (DWORD)cchDirectory)) | ||
173 | { | ||
174 | ExitFunction1(hr = S_FALSE); | ||
175 | } | ||
176 | |||
177 | hr = sczCanonicalizedPath[cchDirectory] ? S_OK : S_FALSE; | ||
178 | |||
100 | LExit: | 179 | LExit: |
101 | ReleaseStr(sczOriginalPath); | 180 | ReleaseStr(sczCanonicalizedPath); |
102 | ReleaseStr(sczOriginalDirectory); | 181 | ReleaseStr(sczCanonicalizedDirectory); |
103 | return hr; | 182 | return hr; |
104 | } | 183 | } |
diff --git a/src/libs/dutil/WixToolset.DUtil/pathutil.cpp b/src/libs/dutil/WixToolset.DUtil/pathutil.cpp index 314eab85..99be003c 100644 --- a/src/libs/dutil/WixToolset.DUtil/pathutil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/pathutil.cpp | |||
@@ -20,6 +20,9 @@ | |||
20 | 20 | ||
21 | #define PATH_GOOD_ENOUGH 64 | 21 | #define PATH_GOOD_ENOUGH 64 |
22 | 22 | ||
23 | static BOOL IsPathSeparatorChar( | ||
24 | __in WCHAR wc | ||
25 | ); | ||
23 | static BOOL IsValidDriveChar( | 26 | static BOOL IsValidDriveChar( |
24 | __in WCHAR wc | 27 | __in WCHAR wc |
25 | ); | 28 | ); |
@@ -41,7 +44,7 @@ DAPI_(LPWSTR) PathFile( | |||
41 | // \ => Windows path | 44 | // \ => Windows path |
42 | // / => unix and URL path | 45 | // / => unix and URL path |
43 | // : => relative path from mapped root | 46 | // : => relative path from mapped root |
44 | if (L'\\' == *wz || L'/' == *wz || (L':' == *wz && wz == wzPath + 1)) | 47 | if (IsPathSeparatorChar(*wz) || (L':' == *wz && wz == wzPath + 1)) |
45 | { | 48 | { |
46 | wzFile = wz + 1; | 49 | wzFile = wz + 1; |
47 | } | 50 | } |
@@ -64,7 +67,7 @@ DAPI_(LPCWSTR) PathExtension( | |||
64 | LPCWSTR wzExtension = NULL; | 67 | LPCWSTR wzExtension = NULL; |
65 | for (LPCWSTR wz = wzPath; *wz; ++wz) | 68 | for (LPCWSTR wz = wzPath; *wz; ++wz) |
66 | { | 69 | { |
67 | if (L'\\' == *wz || L'/' == *wz || L':' == *wz) | 70 | if (IsPathSeparatorChar(*wz) || L':' == *wz) |
68 | { | 71 | { |
69 | wzExtension = NULL; | 72 | wzExtension = NULL; |
70 | } | 73 | } |
@@ -84,7 +87,8 @@ DAPI_(HRESULT) PathGetDirectory( | |||
84 | ) | 87 | ) |
85 | { | 88 | { |
86 | HRESULT hr = S_OK; | 89 | HRESULT hr = S_OK; |
87 | size_t cchDirectory = SIZE_T_MAX; | 90 | LPCWSTR wzRemaining = NULL; |
91 | SIZE_T cchDirectory = 0; | ||
88 | 92 | ||
89 | for (LPCWSTR wz = wzPath; *wz; ++wz) | 93 | for (LPCWSTR wz = wzPath; *wz; ++wz) |
90 | { | 94 | { |
@@ -92,18 +96,20 @@ DAPI_(HRESULT) PathGetDirectory( | |||
92 | // \ => Windows path | 96 | // \ => Windows path |
93 | // / => unix and URL path | 97 | // / => unix and URL path |
94 | // : => relative path from mapped root | 98 | // : => relative path from mapped root |
95 | if (L'\\' == *wz || L'/' == *wz || (L':' == *wz && wz == wzPath + 1)) | 99 | if (IsPathSeparatorChar(*wz) || (L':' == *wz && wz == wzPath + 1)) |
96 | { | 100 | { |
97 | cchDirectory = static_cast<size_t>(wz - wzPath) + 1; | 101 | wzRemaining = wz; |
98 | } | 102 | } |
99 | } | 103 | } |
100 | 104 | ||
101 | if (SIZE_T_MAX == cchDirectory) | 105 | if (!wzRemaining) |
102 | { | 106 | { |
103 | // we were given just a file name, so there's no directory available | 107 | // we were given just a file name, so there's no directory available |
104 | return S_FALSE; | 108 | ExitFunction1(hr = S_FALSE); |
105 | } | 109 | } |
106 | 110 | ||
111 | cchDirectory = static_cast<SIZE_T>(wzRemaining - wzPath) + 1; | ||
112 | |||
107 | hr = StrAllocString(psczDirectory, wzPath, cchDirectory); | 113 | hr = StrAllocString(psczDirectory, wzPath, cchDirectory); |
108 | PathExitOnFailure(hr, "Failed to copy directory."); | 114 | PathExitOnFailure(hr, "Failed to copy directory."); |
109 | 115 | ||
@@ -122,7 +128,7 @@ DAPI_(HRESULT) PathGetParentPath( | |||
122 | 128 | ||
123 | for (LPCWSTR wz = wzPath; *wz; ++wz) | 129 | for (LPCWSTR wz = wzPath; *wz; ++wz) |
124 | { | 130 | { |
125 | if (wz[1] && (L'\\' == *wz || L'/' == *wz)) | 131 | if (IsPathSeparatorChar(*wz) && wz[1]) |
126 | { | 132 | { |
127 | wzParent = wz; | 133 | wzParent = wz; |
128 | } | 134 | } |
@@ -291,12 +297,13 @@ DAPI_(HRESULT) PathPrefix( | |||
291 | hr = StrAllocPrefix(psczFullPath, L"\\\\?\\", 4); | 297 | hr = StrAllocPrefix(psczFullPath, L"\\\\?\\", 4); |
292 | PathExitOnFailure(hr, "Failed to add prefix to file path."); | 298 | PathExitOnFailure(hr, "Failed to add prefix to file path."); |
293 | } | 299 | } |
294 | else if (fFullyQualified && L'\\' == wzFullPath[1]) // UNC | 300 | else if (fFullyQualified && IsPathSeparatorChar(wzFullPath[1])) // UNC |
295 | { | 301 | { |
296 | hr = StrSize(*psczFullPath, &cbFullPath); | 302 | hr = StrSize(*psczFullPath, &cbFullPath); |
297 | PathExitOnFailure(hr, "Failed to get size of full path."); | 303 | PathExitOnFailure(hr, "Failed to get size of full path."); |
298 | 304 | ||
299 | memmove_s(wzFullPath, cbFullPath, wzFullPath + 1, cbFullPath - sizeof(WCHAR)); | 305 | memmove_s(wzFullPath, cbFullPath, wzFullPath + 1, cbFullPath - sizeof(WCHAR)); |
306 | wzFullPath[0] = L'\\'; | ||
300 | 307 | ||
301 | hr = StrAllocPrefix(psczFullPath, L"\\\\?\\UNC", 7); | 308 | hr = StrAllocPrefix(psczFullPath, L"\\\\?\\UNC", 7); |
302 | PathExitOnFailure(hr, "Failed to add prefix to UNC path."); | 309 | PathExitOnFailure(hr, "Failed to add prefix to UNC path."); |
@@ -312,6 +319,90 @@ LExit: | |||
312 | } | 319 | } |
313 | 320 | ||
314 | 321 | ||
322 | DAPI_(HRESULT) PathFixedNormalizeSlashes( | ||
323 | __inout_z LPWSTR wzPath | ||
324 | ) | ||
325 | { | ||
326 | HRESULT hr = S_OK; | ||
327 | size_t cchLength = 0; | ||
328 | BOOL fAllowDoubleSlash = FALSE; | ||
329 | SIZE_T iSource = 0; | ||
330 | SIZE_T jDestination = 0; | ||
331 | |||
332 | hr = ::StringCchLengthW(wzPath, STRSAFE_MAX_CCH, &cchLength); | ||
333 | PathExitOnFailure(hr, "Failed to get length of path."); | ||
334 | |||
335 | if (1 < cchLength && IsPathSeparatorChar(wzPath[0])) | ||
336 | { | ||
337 | if (IsPathSeparatorChar(wzPath[1])) | ||
338 | { | ||
339 | // \\?\\a\ is not equivalent to \\?\a\ and \\server\\a\ is not equivalent to \\server\a\. | ||
340 | fAllowDoubleSlash = TRUE; | ||
341 | wzPath[0] = '\\'; | ||
342 | wzPath[1] = '\\'; | ||
343 | iSource = 2; | ||
344 | jDestination = 2; | ||
345 | } | ||
346 | else if (2 < cchLength && L'?' == wzPath[1] && L'?' == wzPath[2]) | ||
347 | { | ||
348 | // \??\\a\ is not equivalent to \??\a\. | ||
349 | fAllowDoubleSlash = TRUE; | ||
350 | wzPath[0] = '\\'; | ||
351 | wzPath[1] = '?'; | ||
352 | wzPath[2] = '?'; | ||
353 | iSource = 3; | ||
354 | jDestination = 3; | ||
355 | } | ||
356 | } | ||
357 | |||
358 | for (; iSource < cchLength; ++iSource) | ||
359 | { | ||
360 | if (IsPathSeparatorChar(wzPath[iSource])) | ||
361 | { | ||
362 | if (fAllowDoubleSlash) | ||
363 | { | ||
364 | fAllowDoubleSlash = FALSE; | ||
365 | } | ||
366 | else if (IsPathSeparatorChar(wzPath[iSource + 1])) | ||
367 | { | ||
368 | // Skip consecutive slashes. | ||
369 | continue; | ||
370 | } | ||
371 | |||
372 | wzPath[jDestination] = '\\'; | ||
373 | } | ||
374 | else | ||
375 | { | ||
376 | wzPath[jDestination] = wzPath[iSource]; | ||
377 | } | ||
378 | |||
379 | ++jDestination; | ||
380 | } | ||
381 | |||
382 | for (; jDestination < cchLength; ++jDestination) | ||
383 | { | ||
384 | wzPath[jDestination] = '\0'; | ||
385 | } | ||
386 | |||
387 | LExit: | ||
388 | return hr; | ||
389 | } | ||
390 | |||
391 | |||
392 | DAPI_(void) PathFixedReplaceForwardSlashes( | ||
393 | __inout_z LPWSTR wzPath | ||
394 | ) | ||
395 | { | ||
396 | for (LPWSTR wz = wzPath; *wz; ++wz) | ||
397 | { | ||
398 | if (L'/' == *wz) | ||
399 | { | ||
400 | *wz = L'\\'; | ||
401 | } | ||
402 | } | ||
403 | } | ||
404 | |||
405 | |||
315 | DAPI_(HRESULT) PathFixedBackslashTerminate( | 406 | DAPI_(HRESULT) PathFixedBackslashTerminate( |
316 | __inout_ecount_z(cchPath) LPWSTR wzPath, | 407 | __inout_ecount_z(cchPath) LPWSTR wzPath, |
317 | __in SIZE_T cchPath | 408 | __in SIZE_T cchPath |
@@ -320,17 +411,28 @@ DAPI_(HRESULT) PathFixedBackslashTerminate( | |||
320 | HRESULT hr = S_OK; | 411 | HRESULT hr = S_OK; |
321 | size_t cchLength = 0; | 412 | size_t cchLength = 0; |
322 | 413 | ||
414 | if (!cchPath) | ||
415 | { | ||
416 | ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)); | ||
417 | } | ||
418 | |||
323 | hr = ::StringCchLengthW(wzPath, cchPath, &cchLength); | 419 | hr = ::StringCchLengthW(wzPath, cchPath, &cchLength); |
324 | PathExitOnFailure(hr, "Failed to get length of path."); | 420 | PathExitOnFailure(hr, "Failed to get length of path."); |
325 | 421 | ||
326 | if (cchLength >= cchPath) | 422 | LPWSTR wzLast = wzPath + (cchLength - 1); |
423 | if (cchLength && L'/' == wzLast[0]) | ||
327 | { | 424 | { |
328 | hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); | 425 | wzLast[0] = L'\\'; |
329 | } | 426 | } |
330 | else if (L'\\' != wzPath[cchLength - 1]) | 427 | else if (!cchLength || L'\\' != wzLast[0]) |
331 | { | 428 | { |
332 | wzPath[cchLength] = L'\\'; | 429 | if (cchLength + 2 > cchPath) |
333 | wzPath[cchLength + 1] = L'\0'; | 430 | { |
431 | ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)); | ||
432 | } | ||
433 | |||
434 | wzLast[1] = L'\\'; | ||
435 | wzLast[2] = L'\0'; | ||
334 | } | 436 | } |
335 | 437 | ||
336 | LExit: | 438 | LExit: |
@@ -339,10 +441,10 @@ LExit: | |||
339 | 441 | ||
340 | 442 | ||
341 | DAPI_(HRESULT) PathBackslashTerminate( | 443 | DAPI_(HRESULT) PathBackslashTerminate( |
342 | __inout LPWSTR* psczPath | 444 | __inout_z LPWSTR* psczPath |
343 | ) | 445 | ) |
344 | { | 446 | { |
345 | Assert(psczPath && *psczPath); | 447 | Assert(psczPath); |
346 | 448 | ||
347 | HRESULT hr = S_OK; | 449 | HRESULT hr = S_OK; |
348 | SIZE_T cchPath = 0; | 450 | SIZE_T cchPath = 0; |
@@ -354,7 +456,12 @@ DAPI_(HRESULT) PathBackslashTerminate( | |||
354 | hr = ::StringCchLengthW(*psczPath, cchPath, &cchLength); | 456 | hr = ::StringCchLengthW(*psczPath, cchPath, &cchLength); |
355 | PathExitOnFailure(hr, "Failed to get length of path."); | 457 | PathExitOnFailure(hr, "Failed to get length of path."); |
356 | 458 | ||
357 | if (L'\\' != (*psczPath)[cchLength - 1]) | 459 | LPWSTR wzLast = *psczPath + (cchLength - 1); |
460 | if (cchLength && L'/' == wzLast[0]) | ||
461 | { | ||
462 | wzLast[0] = L'\\'; | ||
463 | } | ||
464 | else if (!cchLength || L'\\' != wzLast[0]) | ||
358 | { | 465 | { |
359 | hr = StrAllocConcat(psczPath, L"\\", 1); | 466 | hr = StrAllocConcat(psczPath, L"\\", 1); |
360 | PathExitOnFailure(hr, "Failed to concat backslash onto string."); | 467 | PathExitOnFailure(hr, "Failed to concat backslash onto string."); |
@@ -833,13 +940,13 @@ DAPI_(BOOL) PathIsFullyQualified( | |||
833 | ExitFunction(); | 940 | ExitFunction(); |
834 | } | 941 | } |
835 | 942 | ||
836 | if (L'\\' != wzPath[0]) | 943 | if (!IsPathSeparatorChar(wzPath[0])) |
837 | { | 944 | { |
838 | // The only way to specify a fully qualified path that doesn't begin with a slash | 945 | // The only way to specify a fully qualified path that doesn't begin with a slash |
839 | // is the drive, colon, slash format (C:\). | 946 | // is the drive, colon, slash format (C:\). |
840 | if (IsValidDriveChar(wzPath[0]) && | 947 | if (IsValidDriveChar(wzPath[0]) && |
841 | L':' == wzPath[1] && | 948 | L':' == wzPath[1] && |
842 | L'\\' == wzPath[2]) | 949 | IsPathSeparatorChar(wzPath[2])) |
843 | { | 950 | { |
844 | fFullyQualified = TRUE; | 951 | fFullyQualified = TRUE; |
845 | } | 952 | } |
@@ -849,14 +956,14 @@ DAPI_(BOOL) PathIsFullyQualified( | |||
849 | 956 | ||
850 | // Non-drive fully qualified paths must start with \\ or \?. | 957 | // Non-drive fully qualified paths must start with \\ or \?. |
851 | // \??\ is an archaic form of \\?\. | 958 | // \??\ is an archaic form of \\?\. |
852 | if (L'?' != wzPath[1] && L'\\' != wzPath[1]) | 959 | if (L'?' != wzPath[1] && !IsPathSeparatorChar(wzPath[1])) |
853 | { | 960 | { |
854 | ExitFunction(); | 961 | ExitFunction(); |
855 | } | 962 | } |
856 | 963 | ||
857 | fFullyQualified = TRUE; | 964 | fFullyQualified = TRUE; |
858 | 965 | ||
859 | if (L'?' == wzPath[2] && L'\\' == wzPath[3]) | 966 | if (L'?' == wzPath[2] && IsPathSeparatorChar(wzPath[3])) |
860 | { | 967 | { |
861 | fHasLongPathPrefix = TRUE; | 968 | fHasLongPathPrefix = TRUE; |
862 | } | 969 | } |
@@ -877,7 +984,7 @@ DAPI_(BOOL) PathIsRooted( | |||
877 | ) | 984 | ) |
878 | { | 985 | { |
879 | return wzPath && | 986 | return wzPath && |
880 | (wzPath[0] == L'\\' || | 987 | (IsPathSeparatorChar(wzPath[0]) || |
881 | IsValidDriveChar(wzPath[0]) && wzPath[1] == L':'); | 988 | IsValidDriveChar(wzPath[0]) && wzPath[1] == L':'); |
882 | } | 989 | } |
883 | 990 | ||
@@ -1008,20 +1115,25 @@ DAPI_(HRESULT) PathGetHierarchyArray( | |||
1008 | 1115 | ||
1009 | for (size_t i = 0; i < cchPath; ++i) | 1116 | for (size_t i = 0; i < cchPath; ++i) |
1010 | { | 1117 | { |
1011 | if (wzPath[i] == L'\\') | 1118 | if (IsPathSeparatorChar(wzPath[i])) |
1012 | { | 1119 | { |
1013 | ++cArraySpacesNeeded; | 1120 | ++cArraySpacesNeeded; |
1014 | } | 1121 | } |
1015 | } | 1122 | } |
1016 | 1123 | ||
1017 | if (wzPath[cchPath - 1] != L'\\') | 1124 | if (!IsPathSeparatorChar(wzPath[cchPath - 1])) |
1018 | { | 1125 | { |
1019 | ++cArraySpacesNeeded; | 1126 | ++cArraySpacesNeeded; |
1020 | } | 1127 | } |
1021 | 1128 | ||
1022 | // If it's a UNC path, cut off the first three paths, 2 because it starts with a double backslash, and another because the first ("\\servername\") isn't a path. | 1129 | // If it's a UNC path, cut off the first three paths, 2 because it starts with a double backslash, and another because the first ("\\servername\") isn't a path. |
1023 | if (wzPath[0] == L'\\' && wzPath[1] == L'\\') | 1130 | if (IsPathSeparatorChar(wzPath[0]) && IsPathSeparatorChar(wzPath[1])) |
1024 | { | 1131 | { |
1132 | if (3 > cArraySpacesNeeded) | ||
1133 | { | ||
1134 | ExitFunction1(hr = E_INVALIDARG); | ||
1135 | } | ||
1136 | |||
1025 | cArraySpacesNeeded -= 3; | 1137 | cArraySpacesNeeded -= 3; |
1026 | } | 1138 | } |
1027 | 1139 | ||
@@ -1042,7 +1154,7 @@ DAPI_(HRESULT) PathGetHierarchyArray( | |||
1042 | DWORD cchPathCopy = lstrlenW(sczPathCopy); | 1154 | DWORD cchPathCopy = lstrlenW(sczPathCopy); |
1043 | 1155 | ||
1044 | // If it ends in a backslash, it's a directory path, so cut off everything the last backslash before we get the directory portion of the path | 1156 | // If it ends in a backslash, it's a directory path, so cut off everything the last backslash before we get the directory portion of the path |
1045 | if (wzPath[cchPathCopy - 1] == L'\\') | 1157 | if (IsPathSeparatorChar(wzPath[cchPathCopy - 1])) |
1046 | { | 1158 | { |
1047 | sczPathCopy[cchPathCopy - 1] = L'\0'; | 1159 | sczPathCopy[cchPathCopy - 1] = L'\0'; |
1048 | } | 1160 | } |
@@ -1063,6 +1175,13 @@ LExit: | |||
1063 | return hr; | 1175 | return hr; |
1064 | } | 1176 | } |
1065 | 1177 | ||
1178 | static BOOL IsPathSeparatorChar( | ||
1179 | __in WCHAR wc | ||
1180 | ) | ||
1181 | { | ||
1182 | return L'/' == wc || L'\\' == wc; | ||
1183 | } | ||
1184 | |||
1066 | static BOOL IsValidDriveChar( | 1185 | static BOOL IsValidDriveChar( |
1067 | __in WCHAR wc | 1186 | __in WCHAR wc |
1068 | ) | 1187 | ) |