aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2022-05-26 17:33:41 -0500
committerSean Hall <r.sean.hall@gmail.com>2022-05-31 13:20:44 -0500
commitdea25f58e6119ef1acc5c5cc2a7c98e52cdff519 (patch)
tree853368043c98c9f8617744f8d8cb01b7a629e8a9 /src
parentfb4f8c7108f43d2341ba299424646c4963b21188 (diff)
downloadwix-dea25f58e6119ef1acc5c5cc2a7c98e52cdff519.tar.gz
wix-dea25f58e6119ef1acc5c5cc2a7c98e52cdff519.tar.bz2
wix-dea25f58e6119ef1acc5c5cc2a7c98e52cdff519.zip
Add PathCanonicalizeForComparison.
Diffstat (limited to 'src')
-rw-r--r--src/burn/engine/variable.cpp2
-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
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj2
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp554
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/PayloadFixture.cs2
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/Payload/CanonicalizeName.wxs2
8 files changed, 822 insertions, 116 deletions
diff --git a/src/burn/engine/variable.cpp b/src/burn/engine/variable.cpp
index 718751e5..c96e9d95 100644
--- a/src/burn/engine/variable.cpp
+++ b/src/burn/engine/variable.cpp
@@ -1969,7 +1969,7 @@ static HRESULT InitializeVariableSystemFolder(
1969{ 1969{
1970 HRESULT hr = S_OK; 1970 HRESULT hr = S_OK;
1971 BOOL f64 = (BOOL)dwpData; 1971 BOOL f64 = (BOOL)dwpData;
1972 WCHAR wzSystemFolder[MAX_PATH] = { }; 1972 WCHAR wzSystemFolder[MAX_PATH + 2] = { };
1973 1973
1974#if !defined(_WIN64) 1974#if !defined(_WIN64)
1975 BOOL fIsWow64 = FALSE; 1975 BOOL fIsWow64 = FALSE;
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 )
diff --git a/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj b/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj
index 1c821a7c..c37bdad1 100644
--- a/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj
+++ b/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj
@@ -40,7 +40,7 @@
40 40
41 <PropertyGroup> 41 <PropertyGroup>
42 <ProjectAdditionalIncludeDirectories>..\..\WixToolset.DUtil\inc</ProjectAdditionalIncludeDirectories> 42 <ProjectAdditionalIncludeDirectories>..\..\WixToolset.DUtil\inc</ProjectAdditionalIncludeDirectories>
43 <ProjectAdditionalLinkLibraries>rpcrt4.lib;Mpr.lib;Ws2_32.lib;urlmon.lib;wininet.lib</ProjectAdditionalLinkLibraries> 43 <ProjectAdditionalLinkLibraries>rpcrt4.lib;Mpr.lib;Ws2_32.lib;shlwapi.lib;urlmon.lib;wininet.lib</ProjectAdditionalLinkLibraries>
44 </PropertyGroup> 44 </PropertyGroup>
45 45
46 <ItemGroup> 46 <ItemGroup>
diff --git a/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp
index 65856514..04d0b447 100644
--- a/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp
+++ b/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp
@@ -12,6 +12,290 @@ namespace DutilTests
12 { 12 {
13 public: 13 public:
14 [Fact] 14 [Fact]
15 void PathBackslashFixedTerminateTest()
16 {
17 HRESULT hr = S_OK;
18 WCHAR wzEmpty[1] = { L'\0' };
19 WCHAR wzSingleLetter[1] = { L'a' };
20 WCHAR wzSingleBackslash[1] = { L'\\' };
21 WCHAR wzSingleForwardSlash[1] = { L'/' };
22 WCHAR wzSingleLetterNullTerminated[2] = { L'a', L'\0' };
23 WCHAR wzSingleBackslashNullTerminated[2] = { L'\\', L'\0' };
24 WCHAR wzSingleForwardSlashNullTerminated[2] = { L'/', L'\0' };
25 WCHAR wzExtraSpaceLetterNullTerminated[3] = { L'a', L'\0', L'\0' };
26 WCHAR wzExtraSpaceBackslashNullTerminated[3] = { L'\\', L'\0', L'\0' };
27 WCHAR wzExtraSpaceForwardSlashNullTerminated[3] = { L'/', L'\0', L'\0' };
28
29 hr = PathFixedBackslashTerminate(wzEmpty, 0);
30 NativeAssert::SpecificReturnCode(E_INSUFFICIENT_BUFFER, hr, "PathFixedBackslashTerminate: zero-length, {0}", wzEmpty);
31
32 hr = PathFixedBackslashTerminate(wzEmpty, countof(wzEmpty));
33 NativeAssert::SpecificReturnCode(E_INSUFFICIENT_BUFFER, hr, "PathFixedBackslashTerminate: '' (length 1), {0}", wzEmpty);
34
35 hr = PathFixedBackslashTerminate(wzSingleLetter, countof(wzSingleLetter));
36 NativeAssert::SpecificReturnCode(E_INVALIDARG, hr, "PathFixedBackslashTerminate: 'a' (length 1)");
37
38 hr = PathFixedBackslashTerminate(wzSingleBackslash, countof(wzSingleBackslash));
39 NativeAssert::SpecificReturnCode(E_INVALIDARG, hr, "PathFixedBackslashTerminate: '\\' (length 1)");
40
41 hr = PathFixedBackslashTerminate(wzSingleForwardSlash, countof(wzSingleForwardSlash));
42 NativeAssert::SpecificReturnCode(E_INVALIDARG, hr, "PathFixedBackslashTerminate: '/' (length 1)");
43
44 hr = PathFixedBackslashTerminate(wzSingleLetterNullTerminated, countof(wzSingleLetterNullTerminated));
45 NativeAssert::SpecificReturnCode(E_INSUFFICIENT_BUFFER, hr, "PathFixedBackslashTerminate: 'a' (length 2)");
46
47 hr = PathFixedBackslashTerminate(wzSingleBackslashNullTerminated, countof(wzSingleBackslashNullTerminated));
48 NativeAssert::Succeeded(hr, "PathFixedBackslashTerminate: '\\' (length 2)");
49 NativeAssert::StringEqual(L"\\", wzSingleBackslashNullTerminated);
50
51 hr = PathFixedBackslashTerminate(wzSingleForwardSlashNullTerminated, countof(wzSingleForwardSlashNullTerminated));
52 NativeAssert::Succeeded(hr, "PathFixedBackslashTerminate: '/' (length 2)");
53 NativeAssert::StringEqual(L"\\", wzSingleForwardSlashNullTerminated);
54
55 hr = PathFixedBackslashTerminate(wzExtraSpaceLetterNullTerminated, countof(wzExtraSpaceLetterNullTerminated));
56 NativeAssert::Succeeded(hr, "PathFixedBackslashTerminate: 'a' (length 3)");
57 NativeAssert::StringEqual(L"a\\", wzExtraSpaceLetterNullTerminated);
58
59 hr = PathFixedBackslashTerminate(wzExtraSpaceBackslashNullTerminated, countof(wzExtraSpaceBackslashNullTerminated));
60 NativeAssert::Succeeded(hr, "PathFixedBackslashTerminate: '\\' (length 3)");
61 NativeAssert::StringEqual(L"\\", wzExtraSpaceBackslashNullTerminated);
62
63 hr = PathFixedBackslashTerminate(wzExtraSpaceForwardSlashNullTerminated, countof(wzExtraSpaceForwardSlashNullTerminated));
64 NativeAssert::Succeeded(hr, "PathFixedBackslashTerminate: '/' (length 3)");
65 NativeAssert::StringEqual(L"\\", wzExtraSpaceForwardSlashNullTerminated);
66 }
67
68 [Fact]
69 void PathBackslashTerminateTest()
70 {
71 HRESULT hr = S_OK;
72 LPWSTR sczPath = NULL;
73 LPCWSTR rgwzPaths[16] =
74 {
75 L"", L"\\",
76 L"a", L"a\\",
77 L"\\", L"\\",
78 L"a\\", L"a\\",
79 L"/", L"\\",
80 L"a/", L"a\\",
81 L"\\\\", L"\\\\",
82 L"//", L"/\\",
83 };
84
85 try
86 {
87 for (DWORD i = 0; i < countof(rgwzPaths); i += 2)
88 {
89 hr = StrAllocString(&sczPath, rgwzPaths[i], 0);
90 NativeAssert::Succeeded(hr, "Failed to copy string");
91
92 hr = PathBackslashTerminate(&sczPath);
93 NativeAssert::Succeeded(hr, "PathBackslashTerminate: {0}", rgwzPaths[i]);
94 NativeAssert::StringEqual(rgwzPaths[i + 1], sczPath);
95 }
96 }
97 finally
98 {
99 ReleaseStr(sczPath);
100 }
101 }
102
103 [Fact]
104 void PathCanonicalizeForComparisonTest()
105 {
106 HRESULT hr = S_OK;
107 LPWSTR sczCanonicalized = NULL;
108
109 try
110 {
111 hr = PathCanonicalizeForComparison(L"C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", 0, &sczCanonicalized);
112 Assert::Equal<HRESULT>(HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE), hr);
113
114 hr = PathCanonicalizeForComparison(L"\\\\?\\C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", 0, &sczCanonicalized);
115 Assert::Equal<HRESULT>(HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE), hr);
116
117 hr = PathCanonicalizeForComparison(L"\\\\server", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized);
118 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
119 NativeAssert::StringEqual(L"\\\\server", sczCanonicalized);
120
121 hr = PathCanonicalizeForComparison(L"\\\\server", 0, &sczCanonicalized);
122 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
123 NativeAssert::StringEqual(L"\\\\server", sczCanonicalized);
124
125 hr = PathCanonicalizeForComparison(L"\\\\server\\", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized);
126 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
127 NativeAssert::StringEqual(L"\\\\server\\", sczCanonicalized);
128
129 hr = PathCanonicalizeForComparison(L"\\\\server\\share", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized);
130 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
131 NativeAssert::StringEqual(L"\\\\server\\share", sczCanonicalized);
132
133 hr = PathCanonicalizeForComparison(L"\\\\server\\share\\", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized);
134 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
135 NativeAssert::StringEqual(L"\\\\server\\share\\", sczCanonicalized);
136
137 hr = PathCanonicalizeForComparison(L"\\\\.\\share\\otherdir\\unc.exe", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized);
138 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
139 NativeAssert::StringEqual(L"\\\\.\\share\\otherdir\\unc.exe", sczCanonicalized);
140
141 hr = PathCanonicalizeForComparison(L"\\\\.\\share\\otherdir\\unc.exe", 0, &sczCanonicalized);
142 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
143 NativeAssert::StringEqual(L"\\\\share\\otherdir\\unc.exe", sczCanonicalized);
144
145 hr = PathCanonicalizeForComparison(L"\\\\server\\share\\..\\..\\otherdir\\unc.exe", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized);
146 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
147 NativeAssert::StringEqual(L"\\\\server\\share\\otherdir\\unc.exe", sczCanonicalized);
148
149 hr = PathCanonicalizeForComparison(L"\\\\server\\share\\..\\..\\otherdir\\unc.exe", 0, &sczCanonicalized);
150 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
151 NativeAssert::StringEqual(L"\\\\otherdir\\unc.exe", sczCanonicalized);
152
153 hr = PathCanonicalizeForComparison(L"\\\\?\\UNC\\server\\share\\..\\..\\otherdir\\unc.exe", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized);
154 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
155 NativeAssert::StringEqual(L"\\\\?\\UNC\\server\\share\\otherdir\\unc.exe", sczCanonicalized);
156
157 hr = PathCanonicalizeForComparison(L"\\\\?\\UNC\\server\\share\\..\\..\\otherdir\\unc.exe", 0, &sczCanonicalized);
158 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
159 NativeAssert::StringEqual(L"\\\\otherdir\\unc.exe", sczCanonicalized);
160
161 hr = PathCanonicalizeForComparison(L"C:\\dir\\subdir\\..\\..\\..\\otherdir\\pastroot.exe", 0, &sczCanonicalized);
162 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
163 NativeAssert::StringEqual(L"C:\\otherdir\\pastroot.exe", sczCanonicalized);
164
165 hr = PathCanonicalizeForComparison(L"\\\\?\\C:\\dir\\subdir\\..\\..\\..\\otherdir\\pastroot.exe", 0, &sczCanonicalized);
166 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
167 NativeAssert::StringEqual(L"C:\\otherdir\\pastroot.exe", sczCanonicalized);
168
169 hr = PathCanonicalizeForComparison(L"\\\\?\\C:dir\\subdir\\..\\..\\..\\otherdir\\pastroot.exe", 0, &sczCanonicalized);
170 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
171 NativeAssert::StringEqual(L"\\otherdir\\pastroot.exe", sczCanonicalized);
172
173 hr = PathCanonicalizeForComparison(L"C:dir\\subdir\\..\\..\\..\\otherdir\\pastrelativeroot.exe", 0, &sczCanonicalized);
174 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
175 NativeAssert::StringEqual(L"\\otherdir\\pastrelativeroot.exe", sczCanonicalized);
176
177 hr = PathCanonicalizeForComparison(L"A:dir\\subdir\\..\\..\\otherdir\\relativeroot.exe", 0, &sczCanonicalized);
178 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
179 NativeAssert::StringEqual(L"\\otherdir\\relativeroot.exe", sczCanonicalized);
180
181 hr = PathCanonicalizeForComparison(L"C:dir\\subdir\\otherdir\\relativeroot.exe", 0, &sczCanonicalized);
182 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
183 NativeAssert::StringEqual(L"C:dir\\subdir\\otherdir\\relativeroot.exe", sczCanonicalized);
184
185 hr = PathCanonicalizeForComparison(L"C:\\dir\\subdir\\..\\..\\otherdir\\backslashes.exe", 0, &sczCanonicalized);
186 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
187 NativeAssert::StringEqual(L"C:\\otherdir\\backslashes.exe", sczCanonicalized);
188
189 hr = PathCanonicalizeForComparison(L"C:\\dir\\subdir\\..\\..\\otherdir\\\\consecutivebackslashes.exe", 0, &sczCanonicalized);
190 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
191 NativeAssert::StringEqual(L"C:\\otherdir\\consecutivebackslashes.exe", sczCanonicalized);
192
193 hr = PathCanonicalizeForComparison(L"C:/dir/subdir/../../otherdir/forwardslashes.exe", 0, &sczCanonicalized);
194 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
195 NativeAssert::StringEqual(L"C:\\otherdir\\forwardslashes.exe", sczCanonicalized);
196
197 hr = PathCanonicalizeForComparison(L"\\\\?\\C:\\test\\..\\validlongpath.exe", 0, &sczCanonicalized);
198 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
199 NativeAssert::StringEqual(L"C:\\validlongpath.exe", sczCanonicalized);
200
201 hr = PathCanonicalizeForComparison(L"\\\\?\\test\\..\\invalidlongpath.exe", 0, &sczCanonicalized);
202 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
203 NativeAssert::StringEqual(L"\\\\?\\invalidlongpath.exe", sczCanonicalized);
204
205 hr = PathCanonicalizeForComparison(L"C:\\.\\invalid:pathchars?.exe", 0, &sczCanonicalized);
206 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
207 NativeAssert::StringEqual(L"C:\\invalid:pathchars?.exe", sczCanonicalized);
208
209 hr = PathCanonicalizeForComparison(L"C:\\addprefix.exe", PATH_CANONICALIZE_APPEND_LONG_PATH_PREFIX, &sczCanonicalized);
210 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
211 NativeAssert::StringEqual(L"\\\\?\\C:\\addprefix.exe", sczCanonicalized);
212
213 hr = PathCanonicalizeForComparison(L"C:\\addbackslash.exe", PATH_CANONICALIZE_BACKSLASH_TERMINATE, &sczCanonicalized);
214 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
215 NativeAssert::StringEqual(L"C:\\addbackslash.exe\\", sczCanonicalized);
216 }
217 finally
218 {
219 ReleaseStr(sczCanonicalized);
220 }
221 }
222
223 [Fact]
224 void PathDirectoryContainsPathTest()
225 {
226 HRESULT hr = S_OK;
227
228 hr = PathDirectoryContainsPath(L"", L"");
229 Assert::Equal<HRESULT>(E_INVALIDARG, hr);
230
231 hr = PathDirectoryContainsPath(L"C:\\Directory", L"");
232 Assert::Equal<HRESULT>(E_INVALIDARG, hr);
233
234 hr = PathDirectoryContainsPath(L"", L"C:\\Directory");
235 Assert::Equal<HRESULT>(E_INVALIDARG, hr);
236
237 hr = PathDirectoryContainsPath(L"C:\\Directory", L"C:\\Directory");
238 Assert::Equal<HRESULT>(S_FALSE, hr);
239
240 hr = PathDirectoryContainsPath(L"C:\\Dir", L"C:\\Directory");
241 Assert::Equal<HRESULT>(S_FALSE, hr);
242
243 hr = PathDirectoryContainsPath(L"C:\\Directory", L"C:\\");
244 Assert::Equal<HRESULT>(S_FALSE, hr);
245
246 hr = PathDirectoryContainsPath(L"C:\\Directory", L"C:\\DirectoryPlus");
247 Assert::Equal<HRESULT>(S_FALSE, hr);
248
249 hr = PathDirectoryContainsPath(L"C:\\Directory\\", L"C:\\DirectoryPlus");
250 Assert::Equal<HRESULT>(S_FALSE, hr);
251
252 hr = PathDirectoryContainsPath(L"C:\\Directory\\", L"C:\\Directory\\../Plus");
253 Assert::Equal<HRESULT>(S_FALSE, hr);
254
255 hr = PathDirectoryContainsPath(L"C:\\Directory\\", L"C:\\Directory/../Plus");
256 Assert::Equal<HRESULT>(S_FALSE, hr);
257
258 hr = PathDirectoryContainsPath(L"\\\\server\\share\\Directory", L"\\\\server\\share\\DirectoryPlus");
259 Assert::Equal<HRESULT>(S_FALSE, hr);
260
261 hr = PathDirectoryContainsPath(L"\\\\server\\share\\Directory", L"\\\\discarded\\..\\server\\share\\Directory\\Plus");
262 Assert::Equal<HRESULT>(S_FALSE, hr);
263
264 hr = PathDirectoryContainsPath(L"..\\..", L"..\\..\\plus");
265 Assert::Equal<HRESULT>(E_INVALIDARG, hr);
266
267 hr = PathDirectoryContainsPath(L"..\\..", L"\\..\\..\\plus");
268 Assert::Equal<HRESULT>(E_INVALIDARG, hr);
269
270 hr = PathDirectoryContainsPath(L"\\..\\..", L"\\..\\..\\plus");
271 Assert::Equal<HRESULT>(E_INVALIDARG, hr);
272
273 hr = PathDirectoryContainsPath(L"C:..\\..", L"C:..\\..\\plus");
274 Assert::Equal<HRESULT>(E_INVALIDARG, hr);
275
276 hr = PathDirectoryContainsPath(L"\\\\server\\share\\Directory", L"\\\\server\\share\\Directory\\Plus");
277 Assert::Equal<HRESULT>(S_OK, hr);
278
279 hr = PathDirectoryContainsPath(L"C:\\Directory", L"C:\\directory\\plus");
280 Assert::Equal<HRESULT>(S_OK, hr);
281
282 hr = PathDirectoryContainsPath(L"C:\\Directory\\", L"C:\\Directory\\Plus");
283 Assert::Equal<HRESULT>(S_OK, hr);
284
285 hr = PathDirectoryContainsPath(L"C:\\Directory", L"C:\\.\\Directory\\Plus");
286 Assert::Equal<HRESULT>(S_OK, hr);
287
288 hr = PathDirectoryContainsPath(L"C:\\Directory", L"C:\\Directory/Plus");
289 Assert::Equal<HRESULT>(S_OK, hr);
290
291 hr = PathDirectoryContainsPath(L"C:\\Directory\\", L"C:\\Directory/Plus");
292 Assert::Equal<HRESULT>(S_OK, hr);
293
294 hr = PathDirectoryContainsPath(L"\\\\?\\C:\\Directory", L"C:\\Directory\\Plus");
295 Assert::Equal<HRESULT>(S_OK, hr);
296 }
297
298 [Fact]
15 void PathGetDirectoryTest() 299 void PathGetDirectoryTest()
16 { 300 {
17 HRESULT hr = S_OK; 301 HRESULT hr = S_OK;
@@ -103,6 +387,57 @@ namespace DutilTests
103 NativeAssert::StringEqual(L"Software\\Microsoft\\", rgsczPaths[1]); 387 NativeAssert::StringEqual(L"Software\\Microsoft\\", rgsczPaths[1]);
104 NativeAssert::StringEqual(L"Software\\Microsoft\\Windows\\", rgsczPaths[2]); 388 NativeAssert::StringEqual(L"Software\\Microsoft\\Windows\\", rgsczPaths[2]);
105 ReleaseNullStrArray(rgsczPaths, cPaths); 389 ReleaseNullStrArray(rgsczPaths, cPaths);
390
391 hr = PathGetHierarchyArray(L"c:/foo/bar/bas/a.txt", &rgsczPaths, &cPaths);
392 NativeAssert::Succeeded(hr, "Failed to get parent directories array for regular file path");
393 Assert::Equal<DWORD>(5, cPaths);
394 NativeAssert::StringEqual(L"c:/", rgsczPaths[0]);
395 NativeAssert::StringEqual(L"c:/foo/", rgsczPaths[1]);
396 NativeAssert::StringEqual(L"c:/foo/bar/", rgsczPaths[2]);
397 NativeAssert::StringEqual(L"c:/foo/bar/bas/", rgsczPaths[3]);
398 NativeAssert::StringEqual(L"c:/foo/bar/bas/a.txt", rgsczPaths[4]);
399 ReleaseNullStrArray(rgsczPaths, cPaths);
400
401 hr = PathGetHierarchyArray(L"c:/foo/bar/bas/", &rgsczPaths, &cPaths);
402 NativeAssert::Succeeded(hr, "Failed to get parent directories array for regular directory path");
403 Assert::Equal<DWORD>(4, cPaths);
404 NativeAssert::StringEqual(L"c:/", rgsczPaths[0]);
405 NativeAssert::StringEqual(L"c:/foo/", rgsczPaths[1]);
406 NativeAssert::StringEqual(L"c:/foo/bar/", rgsczPaths[2]);
407 NativeAssert::StringEqual(L"c:/foo/bar/bas/", rgsczPaths[3]);
408 ReleaseNullStrArray(rgsczPaths, cPaths);
409
410 hr = PathGetHierarchyArray(L"//server/share/subdir/file.txt", &rgsczPaths, &cPaths);
411 NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC file path");
412 Assert::Equal<DWORD>(3, cPaths);
413 NativeAssert::StringEqual(L"//server/share/", rgsczPaths[0]);
414 NativeAssert::StringEqual(L"//server/share/subdir/", rgsczPaths[1]);
415 NativeAssert::StringEqual(L"//server/share/subdir/file.txt", rgsczPaths[2]);
416 ReleaseNullStrArray(rgsczPaths, cPaths);
417
418 hr = PathGetHierarchyArray(L"//server/share/subdir/", &rgsczPaths, &cPaths);
419 NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC directory path");
420 Assert::Equal<DWORD>(2, cPaths);
421 NativeAssert::StringEqual(L"//server/share/", rgsczPaths[0]);
422 NativeAssert::StringEqual(L"//server/share/subdir/", rgsczPaths[1]);
423 ReleaseNullStrArray(rgsczPaths, cPaths);
424
425 hr = PathGetHierarchyArray(L"Software/Microsoft/Windows/ValueName", &rgsczPaths, &cPaths);
426 NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC directory path");
427 Assert::Equal<DWORD>(4, cPaths);
428 NativeAssert::StringEqual(L"Software/", rgsczPaths[0]);
429 NativeAssert::StringEqual(L"Software/Microsoft/", rgsczPaths[1]);
430 NativeAssert::StringEqual(L"Software/Microsoft/Windows/", rgsczPaths[2]);
431 NativeAssert::StringEqual(L"Software/Microsoft/Windows/ValueName", rgsczPaths[3]);
432 ReleaseNullStrArray(rgsczPaths, cPaths);
433
434 hr = PathGetHierarchyArray(L"Software/Microsoft/Windows/", &rgsczPaths, &cPaths);
435 NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC directory path");
436 Assert::Equal<DWORD>(3, cPaths);
437 NativeAssert::StringEqual(L"Software/", rgsczPaths[0]);
438 NativeAssert::StringEqual(L"Software/Microsoft/", rgsczPaths[1]);
439 NativeAssert::StringEqual(L"Software/Microsoft/Windows/", rgsczPaths[2]);
440 ReleaseNullStrArray(rgsczPaths, cPaths);
106 } 441 }
107 finally 442 finally
108 { 443 {
@@ -111,11 +446,65 @@ namespace DutilTests
111 } 446 }
112 447
113 [Fact] 448 [Fact]
449 void PathNormalizeSlashesFixedTest()
450 {
451 HRESULT hr = S_OK;
452 LPWSTR sczPath = NULL;
453 LPCWSTR rgwzPaths[54] =
454 {
455 L"", L"",
456 L"\\", L"\\",
457 L"\\\\", L"\\\\",
458 L"\\\\\\", L"\\\\\\",
459 L"\\\\?\\UNC\\", L"\\\\?\\UNC\\",
460 L"C:\\\\foo2", L"C:\\foo2",
461 L"\\\\?\\C:\\\\foo2", L"\\\\?\\C:\\foo2",
462 L"\\\\a\\b\\", L"\\\\a\\b\\",
463 L"\\\\?\\UNC\\a\\b\\\\c\\", L"\\\\?\\UNC\\a\\b\\c\\",
464 L"\\\\?\\UNC\\a\\b\\\\", L"\\\\?\\UNC\\a\\b\\",
465 L"\\\\?\\UNC\\test\\unc\\path\\to\\\\something", L"\\\\?\\UNC\\test\\unc\\path\\to\\something",
466 L"\\\\?\\C:\\\\foo\\\\bar.txt", L"\\\\?\\C:\\foo\\bar.txt",
467 L"\\??\\C:\\\\foo\\bar.txt", L"\\??\\C:\\foo\\bar.txt",
468 L"\\??\\\\C:\\\\foo\\bar.txt", L"\\??\\\\C:\\foo\\bar.txt",
469 L"/", L"\\",
470 L"//", L"\\\\",
471 L"///", L"\\\\\\",
472 L"//?/UNC/", L"\\\\?\\UNC\\",
473 L"C://foo2", L"C:\\foo2",
474 L"//?/C://foo2", L"\\\\?\\C:\\foo2",
475 L"//a/b/", L"\\\\a\\b\\",
476 L"//?/UNC/a/b//c/", L"\\\\?\\UNC\\a\\b\\c\\",
477 L"//?/UNC/a/b//", L"\\\\?\\UNC\\a\\b\\",
478 L"//?/UNC/test/unc/path/to//something", L"\\\\?\\UNC\\test\\unc\\path\\to\\something",
479 L"//?/C://foo//bar.txt", L"\\\\?\\C:\\foo\\bar.txt",
480 L"/??/C://foo/bar.txt", L"\\??\\C:\\foo\\bar.txt",
481 L"/??//C://foo/bar.txt", L"\\??\\\\C:\\foo\\bar.txt",
482 };
483
484 try
485 {
486 for (DWORD i = 0; i < countof(rgwzPaths); i += 2)
487 {
488 hr = StrAllocString(&sczPath, rgwzPaths[i], 0);
489 NativeAssert::Succeeded(hr, "Failed to copy string");
490
491 hr = PathFixedNormalizeSlashes(sczPath);
492 NativeAssert::Succeeded(hr, "PathNormalizeSlashes: {0}", rgwzPaths[i]);
493 NativeAssert::StringEqual(rgwzPaths[i + 1], sczPath);
494 }
495 }
496 finally
497 {
498 ReleaseStr(sczPath);
499 }
500 }
501
502 [Fact]
114 void PathPrefixTest() 503 void PathPrefixTest()
115 { 504 {
116 HRESULT hr = S_OK; 505 HRESULT hr = S_OK;
117 LPWSTR sczPath = NULL; 506 LPWSTR sczPath = NULL;
118 LPCWSTR rgwzPaths[12] = 507 LPCWSTR rgwzPaths[24] =
119 { 508 {
120 L"\\\\", L"\\\\?\\UNC\\", 509 L"\\\\", L"\\\\?\\UNC\\",
121 L"C:\\\\foo2", L"\\\\?\\C:\\\\foo2", 510 L"C:\\\\foo2", L"\\\\?\\C:\\\\foo2",
@@ -123,11 +512,17 @@ namespace DutilTests
123 L"\\\\?\\UNC\\test\\unc\\path\\to\\something", L"\\\\?\\UNC\\test\\unc\\path\\to\\something", 512 L"\\\\?\\UNC\\test\\unc\\path\\to\\something", L"\\\\?\\UNC\\test\\unc\\path\\to\\something",
124 L"\\\\?\\C:\\foo\\bar.txt", L"\\\\?\\C:\\foo\\bar.txt", 513 L"\\\\?\\C:\\foo\\bar.txt", L"\\\\?\\C:\\foo\\bar.txt",
125 L"\\??\\C:\\foo\\bar.txt", L"\\??\\C:\\foo\\bar.txt", 514 L"\\??\\C:\\foo\\bar.txt", L"\\??\\C:\\foo\\bar.txt",
515 L"//", L"\\\\?\\UNC\\",
516 L"C://foo2", L"\\\\?\\C://foo2",
517 L"//a/b/", L"\\\\?\\UNC\\a/b/",
518 L"//?/UNC/test/unc/path/to/something", L"//?/UNC/test/unc/path/to/something",
519 L"//?/C:/foo/bar.txt", L"//?/C:/foo/bar.txt",
520 L"/??/C:/foo/bar.txt", L"/??/C:/foo/bar.txt",
126 }; 521 };
127 522
128 try 523 try
129 { 524 {
130 for (DWORD i = 0; i < countof(rgwzPaths) / 2; i += 2) 525 for (DWORD i = 0; i < countof(rgwzPaths); i += 2)
131 { 526 {
132 hr = StrAllocString(&sczPath, rgwzPaths[i], 0); 527 hr = StrAllocString(&sczPath, rgwzPaths[i], 0);
133 NativeAssert::Succeeded(hr, "Failed to copy string"); 528 NativeAssert::Succeeded(hr, "Failed to copy string");
@@ -148,16 +543,20 @@ namespace DutilTests
148 { 543 {
149 HRESULT hr = S_OK; 544 HRESULT hr = S_OK;
150 LPWSTR sczPath = NULL; 545 LPWSTR sczPath = NULL;
151 LPCWSTR rgwzPaths[8] = 546 LPCWSTR rgwzPaths[12] =
152 { 547 {
153 L"\\", 548 L"\\",
549 L"/",
154 L"C:", 550 L"C:",
155 L"C:foo.txt", 551 L"C:foo.txt",
156 L"", 552 L"",
157 L"\\?", 553 L"\\?",
554 L"/?",
158 L"\\dir", 555 L"\\dir",
556 L"/dir",
159 L"dir", 557 L"dir",
160 L"dir\\subdir", 558 L"dir\\subdir",
559 L"dir/subdir",
161 }; 560 };
162 561
163 try 562 try
@@ -180,93 +579,162 @@ namespace DutilTests
180 [Fact] 579 [Fact]
181 void PathIsRootedAndFullyQualifiedTest() 580 void PathIsRootedAndFullyQualifiedTest()
182 { 581 {
582 HRESULT hr = S_OK;
583 LPWSTR sczPath = NULL;
183 LPCWSTR rgwzPaths[15] = 584 LPCWSTR rgwzPaths[15] =
184 { 585 {
185 L"\\\\", 586 L"//",
186 L"\\\\\\", 587 L"///",
187 L"C:\\", 588 L"C:/",
188 L"C:\\\\", 589 L"C://",
189 L"C:\\foo1", 590 L"C:/foo1",
190 L"C:\\\\foo2", 591 L"C://foo2",
191 L"\\\\test\\unc\\path\\to\\something", 592 L"//test/unc/path/to/something",
192 L"\\\\a\\b\\c\\d\\e", 593 L"//a/b/c/d/e",
193 L"\\\\a\\b\\", 594 L"//a/b/",
194 L"\\\\a\\b", 595 L"//a/b",
195 L"\\\\test\\unc", 596 L"//test/unc",
196 L"\\\\Server", 597 L"//Server",
197 L"\\\\Server\\Foo.txt", 598 L"//Server/Foo.txt",
198 L"\\\\Server\\Share\\Foo.txt", 599 L"//Server/Share/Foo.txt",
199 L"\\\\Server\\Share\\Test\\Foo.txt", 600 L"//Server/Share/Test/Foo.txt",
200 }; 601 };
201 602
202 for (DWORD i = 0; i < countof(rgwzPaths); ++i) 603 try
203 { 604 {
204 ValidateFullyQualifiedPath(rgwzPaths[i], TRUE, FALSE); 605 for (DWORD i = 0; i < countof(rgwzPaths); ++i)
205 ValidateRootedPath(rgwzPaths[i], TRUE); 606 {
607 ValidateFullyQualifiedPath(rgwzPaths[i], TRUE, FALSE);
608 ValidateRootedPath(rgwzPaths[i], TRUE);
609
610 hr = StrAllocString(&sczPath, rgwzPaths[i], 0);
611 NativeAssert::Succeeded(hr, "Failed to copy string");
612
613 PathFixedReplaceForwardSlashes(sczPath);
614 ValidateFullyQualifiedPath(sczPath, TRUE, FALSE);
615 ValidateRootedPath(sczPath, TRUE);
616 }
617 }
618 finally
619 {
620 ReleaseStr(sczPath);
206 } 621 }
207 } 622 }
208 623
209 [Fact] 624 [Fact]
210 void PathIsRootedAndFullyQualifiedWithPrefixTest() 625 void PathIsRootedAndFullyQualifiedWithPrefixTest()
211 { 626 {
627 HRESULT hr = S_OK;
628 LPWSTR sczPath = NULL;
212 LPCWSTR rgwzPaths[6] = 629 LPCWSTR rgwzPaths[6] =
213 { 630 {
214 L"\\\\?\\UNC\\test\\unc\\path\\to\\something", 631 L"//?/UNC/test/unc/path/to/something",
215 L"\\\\?\\UNC\\test\\unc", 632 L"//?/UNC/test/unc",
216 L"\\\\?\\UNC\\a\\b1", 633 L"//?/UNC/a/b1",
217 L"\\\\?\\UNC\\a\\b2\\", 634 L"//?/UNC/a/b2/",
218 L"\\\\?\\C:\\foo\\bar.txt", 635 L"//?/C:/foo/bar.txt",
219 L"\\??\\C:\\foo\\bar.txt", 636 L"/??/C:/foo/bar.txt",
220 }; 637 };
221 638
222 for (DWORD i = 0; i < countof(rgwzPaths); ++i) 639 try
223 { 640 {
224 ValidateFullyQualifiedPath(rgwzPaths[i], TRUE, TRUE); 641 for (DWORD i = 0; i < countof(rgwzPaths); ++i)
225 ValidateRootedPath(rgwzPaths[i], TRUE); 642 {
643 ValidateFullyQualifiedPath(rgwzPaths[i], TRUE, TRUE);
644 ValidateRootedPath(rgwzPaths[i], TRUE);
645
646 hr = StrAllocString(&sczPath, rgwzPaths[i], 0);
647 NativeAssert::Succeeded(hr, "Failed to copy string");
648
649 PathFixedReplaceForwardSlashes(sczPath);
650 ValidateFullyQualifiedPath(sczPath, TRUE, TRUE);
651 ValidateRootedPath(sczPath, TRUE);
652 }
653 }
654 finally
655 {
656 ReleaseStr(sczPath);
226 } 657 }
227 } 658 }
228 659
229 [Fact] 660 [Fact]
230 void PathIsRootedButNotFullyQualifiedTest() 661 void PathIsRootedButNotFullyQualifiedTest()
231 { 662 {
663 HRESULT hr = S_OK;
664 LPWSTR sczPath = NULL;
232 LPCWSTR rgwzPaths[7] = 665 LPCWSTR rgwzPaths[7] =
233 { 666 {
234 L"\\", 667 L"/",
235 L"a:", 668 L"a:",
236 L"A:", 669 L"A:",
237 L"z:", 670 L"z:",
238 L"Z:", 671 L"Z:",
239 L"C:foo.txt", 672 L"C:foo.txt",
240 L"\\dir", 673 L"/dir",
241 }; 674 };
242 675
243 for (DWORD i = 0; i < countof(rgwzPaths); ++i) 676 try
244 { 677 {
245 ValidateFullyQualifiedPath(rgwzPaths[i], FALSE, FALSE); 678 for (DWORD i = 0; i < countof(rgwzPaths); ++i)
246 ValidateRootedPath(rgwzPaths[i], TRUE); 679 {
680 ValidateFullyQualifiedPath(rgwzPaths[i], FALSE, FALSE);
681 ValidateRootedPath(rgwzPaths[i], TRUE);
682
683 hr = StrAllocString(&sczPath, rgwzPaths[i], 0);
684 NativeAssert::Succeeded(hr, "Failed to copy string");
685
686 PathFixedReplaceForwardSlashes(sczPath);
687 ValidateFullyQualifiedPath(sczPath, FALSE, FALSE);
688 ValidateRootedPath(sczPath, TRUE);
689 }
690 }
691 finally
692 {
693 ReleaseStr(sczPath);
247 } 694 }
248 } 695 }
249 696
250 [Fact] 697 [Fact]
251 void PathIsNotRootedAndNotFullyQualifiedTest() 698 void PathIsNotRootedAndNotFullyQualifiedTest()
252 { 699 {
700 HRESULT hr = S_OK;
701 LPWSTR sczPath = NULL;
253 LPCWSTR rgwzPaths[9] = 702 LPCWSTR rgwzPaths[9] =
254 { 703 {
255 NULL, 704 NULL,
256 L"", 705 L"",
257 L"dir", 706 L"dir",
258 L"dir\\subdir", 707 L"dir/subdir",
259 L"@:\\foo", // 064 = @ 065 = A 708 L"@:/foo", // 064 = @ 065 = A
260 L"[:\\\\", // 091 = [ 090 = Z 709 L"[://", // 091 = [ 090 = Z
261 L"`:\\foo ", // 096 = ` 097 = a 710 L"`:/foo ", // 096 = ` 097 = a
262 L"{:\\\\", // 123 = { 122 = z 711 L"{://", // 123 = { 122 = z
263 L"[:", 712 L"[:",
264 }; 713 };
265 714
266 for (DWORD i = 0; i < countof(rgwzPaths); ++i) 715 try
267 { 716 {
268 ValidateFullyQualifiedPath(rgwzPaths[i], FALSE, FALSE); 717 for (DWORD i = 0; i < countof(rgwzPaths); ++i)
269 ValidateRootedPath(rgwzPaths[i], FALSE); 718 {
719 ValidateFullyQualifiedPath(rgwzPaths[i], FALSE, FALSE);
720 ValidateRootedPath(rgwzPaths[i], FALSE);
721
722 if (!rgwzPaths[i])
723 {
724 continue;
725 }
726
727 hr = StrAllocString(&sczPath, rgwzPaths[i], 0);
728 NativeAssert::Succeeded(hr, "Failed to copy string");
729
730 PathFixedReplaceForwardSlashes(sczPath);
731 ValidateFullyQualifiedPath(sczPath, FALSE, FALSE);
732 ValidateRootedPath(sczPath, FALSE);
733 }
734 }
735 finally
736 {
737 ReleaseStr(sczPath);
270 } 738 }
271 } 739 }
272 740
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/PayloadFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/PayloadFixture.cs
index 5b663d93..a4f6a3fd 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/PayloadFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/PayloadFixture.cs
@@ -84,7 +84,7 @@ namespace WixToolsetTest.CoreIntegration
84 ? field.AsNullableNumber()?.ToString() 84 ? field.AsNullableNumber()?.ToString()
85 : field?.AsString()) 85 : field?.AsString())
86 .ToList(); 86 .ToList();
87 Assert.Equal(@"c\d.exe", fields[(int)WixBundlePayloadSymbolFields.Name]); 87 Assert.Equal(@"c\d\e\f.exe", fields[(int)WixBundlePayloadSymbolFields.Name]);
88 } 88 }
89 } 89 }
90 90
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Payload/CanonicalizeName.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Payload/CanonicalizeName.wxs
index 544b80ec..a6f3b453 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Payload/CanonicalizeName.wxs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Payload/CanonicalizeName.wxs
@@ -1,7 +1,7 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> 1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Fragment> 2 <Fragment>
3 <PayloadGroup Id="CanonicalizeName"> 3 <PayloadGroup Id="CanonicalizeName">
4 <Payload SourceFile="dir\file.ext" Name="a\..\c\.\d.exe" /> 4 <Payload SourceFile="dir\file.ext" Name="a\..\c\.\d/e\\f.exe" />
5 </PayloadGroup> 5 </PayloadGroup>
6 </Fragment> 6 </Fragment>
7</Wix> 7</Wix>