summaryrefslogtreecommitdiff
path: root/src/libs/dutil/WixToolset.DUtil
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil')
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/pathutil.h56
-rw-r--r--src/libs/dutil/WixToolset.DUtil/path2utl.cpp149
-rw-r--r--src/libs/dutil/WixToolset.DUtil/pathutil.cpp171
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 @@
6extern "C" { 6extern "C" {
7#endif 7#endif
8 8
9typedef enum PATH_EXPAND 9typedef 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
19typedef 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********************************************************************/
34DAPI_(HRESULT) PathGetDirectory( 46DAPI_(HRESULT) PathGetDirectory(
35 __in_z LPCWSTR wzPath, 47 __in_z LPCWSTR wzPath,
@@ -38,6 +50,7 @@ DAPI_(HRESULT) PathGetDirectory(
38 50
39/******************************************************************* 51/*******************************************************************
40PathGetParentPath - extracts the parent directory from a full path. 52PathGetParentPath - extracts the parent directory from a full path.
53 *psczDirectory is NULL if the path only contains a file name.
41********************************************************************/ 54********************************************************************/
42DAPI_(HRESULT) PathGetParentPath( 55DAPI_(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********************************************************************/
82DAPI_(HRESULT) PathFixedNormalizeSlashes(
83 __inout_z LPWSTR wzPath
84 );
85
86/*******************************************************************
87 PathFixedReplaceForwardSlashes - replaces all / with \
88********************************************************************/
89DAPI_(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********************************************************************/
79DAPI_(HRESULT) PathBackslashTerminate( 107DAPI_(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*******************************************************************/
174DAPI_(BOOL) PathIsFullyQualified( 202DAPI_(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*******************************************************************/
185DAPI_(BOOL) PathIsRooted( 213DAPI_(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*******************************************************************/
245DAPI_(HRESULT) PathCanonicalizePath( 273DAPI_(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/*******************************************************************
251PathDirectoryContainsPath - 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*******************************************************************/
284DAPI_(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*******************************************************************/
254DAPI_(HRESULT) PathDirectoryContainsPath( 294DAPI_(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
45DAPI_(HRESULT) PathDirectoryContainsPath( 46DAPI_(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
128LExit:
129 ReleaseStr(sczNormalizedPath);
85 130
86 ++sczDirectory; 131 return hr;
87 ++sczPath; 132}
133
134DAPI_(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
100LExit: 179LExit:
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
23static BOOL IsPathSeparatorChar(
24 __in WCHAR wc
25 );
23static BOOL IsValidDriveChar( 26static 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
322DAPI_(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
387LExit:
388 return hr;
389}
390
391
392DAPI_(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
315DAPI_(HRESULT) PathFixedBackslashTerminate( 406DAPI_(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
336LExit: 438LExit:
@@ -339,10 +441,10 @@ LExit:
339 441
340 442
341DAPI_(HRESULT) PathBackslashTerminate( 443DAPI_(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
1178static BOOL IsPathSeparatorChar(
1179 __in WCHAR wc
1180 )
1181{
1182 return L'/' == wc || L'\\' == wc;
1183}
1184
1066static BOOL IsValidDriveChar( 1185static BOOL IsValidDriveChar(
1067 __in WCHAR wc 1186 __in WCHAR wc
1068 ) 1187 )