diff options
Diffstat (limited to '')
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 @@ | |||
| 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 | ) |
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> |
