diff options
| author | Sean Hall <r.sean.hall@gmail.com> | 2022-06-03 17:48:39 -0500 |
|---|---|---|
| committer | Sean Hall <r.sean.hall@gmail.com> | 2022-06-07 19:44:36 -0500 |
| commit | 6b0f2d978504da82070523eb6adb0b59f9812e93 (patch) | |
| tree | 6f0b258519a0f51bf589e4313206b3ffeaa32a41 /src | |
| parent | b652e93a460b4b822a01382e5992f96f1d805ffe (diff) | |
| download | wix-6b0f2d978504da82070523eb6adb0b59f9812e93.tar.gz wix-6b0f2d978504da82070523eb6adb0b59f9812e93.tar.bz2 wix-6b0f2d978504da82070523eb6adb0b59f9812e93.zip | |
Add PathSkipPastRoot.
Diffstat (limited to '')
| -rw-r--r-- | src/burn/engine/bundlepackageengine.cpp | 2 | ||||
| -rw-r--r-- | src/burn/engine/exeengine.cpp | 2 | ||||
| -rw-r--r-- | src/burn/engine/logging.cpp | 6 | ||||
| -rw-r--r-- | src/libs/dutil/WixToolset.DUtil/dirutil.cpp | 20 | ||||
| -rw-r--r-- | src/libs/dutil/WixToolset.DUtil/inc/pathutil.h | 55 | ||||
| -rw-r--r-- | src/libs/dutil/WixToolset.DUtil/logutil.cpp | 2 | ||||
| -rw-r--r-- | src/libs/dutil/WixToolset.DUtil/path2utl.cpp | 53 | ||||
| -rw-r--r-- | src/libs/dutil/WixToolset.DUtil/pathutil.cpp | 296 | ||||
| -rw-r--r-- | src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp | 288 |
9 files changed, 414 insertions, 310 deletions
diff --git a/src/burn/engine/bundlepackageengine.cpp b/src/burn/engine/bundlepackageengine.cpp index 97861436..8ba8e0d2 100644 --- a/src/burn/engine/bundlepackageengine.cpp +++ b/src/burn/engine/bundlepackageengine.cpp | |||
| @@ -763,7 +763,7 @@ static HRESULT ExecuteBundle( | |||
| 763 | 763 | ||
| 764 | if (fPseudoPackage) | 764 | if (fPseudoPackage) |
| 765 | { | 765 | { |
| 766 | if (!PathIsFullyQualified(pPackagePayload->sczFilePath, NULL)) | 766 | if (!PathIsFullyQualified(pPackagePayload->sczFilePath)) |
| 767 | { | 767 | { |
| 768 | ExitWithRootFailure(hr, E_INVALIDSTATE, "Related bundles must have a fully qualified target path."); | 768 | ExitWithRootFailure(hr, E_INVALIDSTATE, "Related bundles must have a fully qualified target path."); |
| 769 | } | 769 | } |
diff --git a/src/burn/engine/exeengine.cpp b/src/burn/engine/exeengine.cpp index c3757e92..fb852c78 100644 --- a/src/burn/engine/exeengine.cpp +++ b/src/burn/engine/exeengine.cpp | |||
| @@ -363,7 +363,7 @@ extern "C" HRESULT ExeEngineExecutePackage( | |||
| 363 | 363 | ||
| 364 | if (pPackage->Exe.fPseudoPackage && BURN_PAYLOAD_VERIFICATION_UPDATE_BUNDLE != pPackagePayload->verification) | 364 | if (pPackage->Exe.fPseudoPackage && BURN_PAYLOAD_VERIFICATION_UPDATE_BUNDLE != pPackagePayload->verification) |
| 365 | { | 365 | { |
| 366 | if (!PathIsFullyQualified(pPackagePayload->sczFilePath, NULL)) | 366 | if (!PathIsFullyQualified(pPackagePayload->sczFilePath)) |
| 367 | { | 367 | { |
| 368 | ExitWithRootFailure(hr, E_INVALIDSTATE, "Pseudo ExePackages must have a fully qualified target path."); | 368 | ExitWithRootFailure(hr, E_INVALIDSTATE, "Pseudo ExePackages must have a fully qualified target path."); |
| 369 | } | 369 | } |
diff --git a/src/burn/engine/logging.cpp b/src/burn/engine/logging.cpp index 3a403025..77f5079c 100644 --- a/src/burn/engine/logging.cpp +++ b/src/burn/engine/logging.cpp | |||
| @@ -134,10 +134,12 @@ extern "C" HRESULT LoggingOpen( | |||
| 134 | 134 | ||
| 135 | if (sczPrefixFormatted && *sczPrefixFormatted) | 135 | if (sczPrefixFormatted && *sczPrefixFormatted) |
| 136 | { | 136 | { |
| 137 | // Best effort to open default logging. | ||
| 137 | LPCWSTR wzPrefix = sczPrefixFormatted; | 138 | LPCWSTR wzPrefix = sczPrefixFormatted; |
| 139 | LPCWSTR wzPastRoot = PathSkipPastRoot(sczPrefixFormatted, NULL, NULL, NULL); | ||
| 138 | 140 | ||
| 139 | // Best effort to open default logging. | 141 | // If the log path is rooted and has a file component, then use that path as is. |
| 140 | if (PathIsRooted(sczPrefixFormatted)) | 142 | if (wzPastRoot && *wzPastRoot) |
| 141 | { | 143 | { |
| 142 | hr = PathGetDirectory(sczPrefixFormatted, &sczLoggingBaseFolder); | 144 | hr = PathGetDirectory(sczPrefixFormatted, &sczLoggingBaseFolder); |
| 143 | ExitOnFailure(hr, "Failed to get parent directory from '%ls'.", sczPrefixFormatted); | 145 | ExitOnFailure(hr, "Failed to get parent directory from '%ls'.", sczPrefixFormatted); |
diff --git a/src/libs/dutil/WixToolset.DUtil/dirutil.cpp b/src/libs/dutil/WixToolset.DUtil/dirutil.cpp index c106a467..94eab9e7 100644 --- a/src/libs/dutil/WixToolset.DUtil/dirutil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/dirutil.cpp | |||
| @@ -364,17 +364,27 @@ extern "C" DWORD DAPI DirDeleteEmptyDirectoriesToRoot( | |||
| 364 | __in DWORD /*dwFlags*/ | 364 | __in DWORD /*dwFlags*/ |
| 365 | ) | 365 | ) |
| 366 | { | 366 | { |
| 367 | HRESULT hr = S_OK; | ||
| 367 | DWORD cDeletedDirs = 0; | 368 | DWORD cDeletedDirs = 0; |
| 368 | LPWSTR sczPath = NULL; | 369 | LPWSTR sczPath = NULL; |
| 370 | LPCWSTR wzPastRoot = NULL; | ||
| 371 | SIZE_T cchRoot = 0; | ||
| 372 | |||
| 373 | // Make sure the path is normalized and prefixed. | ||
| 374 | hr = PathExpand(&sczPath, wzPath, PATH_EXPAND_FULLPATH); | ||
| 375 | DirExitOnFailure(hr, "Failed to get full path for: %ls", wzPath); | ||
| 376 | |||
| 377 | wzPastRoot = PathSkipPastRoot(sczPath, NULL, NULL, NULL); | ||
| 378 | DirExitOnNull(wzPastRoot, hr, E_INVALIDARG, "Full path was not rooted: %ls", sczPath); | ||
| 369 | 379 | ||
| 370 | while (wzPath && *wzPath && ::RemoveDirectoryW(wzPath)) | 380 | cchRoot = wzPastRoot - sczPath; |
| 381 | |||
| 382 | while (sczPath && sczPath[cchRoot] && ::RemoveDirectoryW(sczPath)) | ||
| 371 | { | 383 | { |
| 372 | ++cDeletedDirs; | 384 | ++cDeletedDirs; |
| 373 | 385 | ||
| 374 | HRESULT hr = PathGetParentPath(wzPath, &sczPath); | 386 | hr = PathGetParentPath(sczPath, &sczPath, &cchRoot); |
| 375 | DirExitOnFailure(hr, "Failed to get parent directory for path: %ls", wzPath); | 387 | DirExitOnFailure(hr, "Failed to get parent directory for path: %ls", sczPath); |
| 376 | |||
| 377 | wzPath = sczPath; | ||
| 378 | } | 388 | } |
| 379 | 389 | ||
| 380 | LExit: | 390 | LExit: |
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h b/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h index e64c8ef3..727318f2 100644 --- a/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h +++ b/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h | |||
| @@ -8,8 +8,8 @@ extern "C" { | |||
| 8 | 8 | ||
| 9 | typedef enum _PATH_CANONICALIZE | 9 | typedef enum _PATH_CANONICALIZE |
| 10 | { | 10 | { |
| 11 | // Always prefix fully qualified paths with the long path prefix (\\?\). | 11 | // Always prefix fully qualified paths with the extended path prefix (\\?\). |
| 12 | PATH_CANONICALIZE_APPEND_LONG_PATH_PREFIX = 0x0001, | 12 | PATH_CANONICALIZE_APPEND_EXTENDED_PATH_PREFIX = 0x0001, |
| 13 | // Always terminate the path with \. | 13 | // Always terminate the path with \. |
| 14 | PATH_CANONICALIZE_BACKSLASH_TERMINATE = 0x0002, | 14 | PATH_CANONICALIZE_BACKSLASH_TERMINATE = 0x0002, |
| 15 | // Don't collapse . or .. in the \\server\share portion of a UNC path. | 15 | // Don't collapse . or .. in the \\server\share portion of a UNC path. |
| @@ -22,6 +22,14 @@ typedef enum _PATH_EXPAND | |||
| 22 | PATH_EXPAND_FULLPATH = 0x0002, | 22 | PATH_EXPAND_FULLPATH = 0x0002, |
| 23 | } PATH_EXPAND; | 23 | } PATH_EXPAND; |
| 24 | 24 | ||
| 25 | typedef enum _PATH_PREFIX | ||
| 26 | { | ||
| 27 | // Add prefix even if the path is not longer than MAX_PATH. | ||
| 28 | PATH_PREFIX_SHORT_PATHS = 0x0001, | ||
| 29 | // Error with E_INVALIDARG if the path is not fully qualified. | ||
| 30 | PATH_PREFIX_EXPECT_FULLY_QUALIFIED = 0x0002, | ||
| 31 | } PATH_PREFIX; | ||
| 32 | |||
| 25 | 33 | ||
| 26 | /******************************************************************* | 34 | /******************************************************************* |
| 27 | PathFile - returns a pointer to the file part of the path. | 35 | PathFile - returns a pointer to the file part of the path. |
| @@ -40,8 +48,9 @@ DAPI_(LPCWSTR) PathExtension( | |||
| 40 | 48 | ||
| 41 | /******************************************************************* | 49 | /******************************************************************* |
| 42 | PathGetDirectory - extracts the directory from a path including the directory separator. | 50 | 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. | 51 | Calling the function again with the previous result returns the same result. |
| 44 | Returns S_FALSE if the path only contains a file name. | 52 | Returns S_FALSE if the path only contains a file name. |
| 53 | For example, C:\a\b -> C:\a\ -> C:\a\ | ||
| 45 | ********************************************************************/ | 54 | ********************************************************************/ |
| 46 | DAPI_(HRESULT) PathGetDirectory( | 55 | DAPI_(HRESULT) PathGetDirectory( |
| 47 | __in_z LPCWSTR wzPath, | 56 | __in_z LPCWSTR wzPath, |
| @@ -49,12 +58,18 @@ DAPI_(HRESULT) PathGetDirectory( | |||
| 49 | ); | 58 | ); |
| 50 | 59 | ||
| 51 | /******************************************************************* | 60 | /******************************************************************* |
| 52 | PathGetParentPath - extracts the parent directory from a full path. | 61 | PathGetParentPath - extracts the parent directory from a path |
| 53 | *psczDirectory is NULL if the path only contains a file name. | 62 | ignoring a trailing slash so that when called repeatedly, |
| 63 | it eventually returns the root portion of the path. | ||
| 64 | *psczDirectory is NULL if the path only contains a file name or | ||
| 65 | the path only contains the root. | ||
| 66 | *pcchRoot is the length of the root part of the path. | ||
| 67 | For example, C:\a\b -> C:\a\ -> C:\ -> NULL | ||
| 54 | ********************************************************************/ | 68 | ********************************************************************/ |
| 55 | DAPI_(HRESULT) PathGetParentPath( | 69 | DAPI_(HRESULT) PathGetParentPath( |
| 56 | __in_z LPCWSTR wzPath, | 70 | __in_z LPCWSTR wzPath, |
| 57 | __out_z LPWSTR *psczDirectory | 71 | __out_z LPWSTR *psczDirectory, |
| 72 | __out_opt SIZE_T* pcchRoot | ||
| 58 | ); | 73 | ); |
| 59 | 74 | ||
| 60 | /******************************************************************* | 75 | /******************************************************************* |
| @@ -78,11 +93,14 @@ DAPI_(HRESULT) PathGetFullPathName( | |||
| 78 | ); | 93 | ); |
| 79 | 94 | ||
| 80 | /******************************************************************* | 95 | /******************************************************************* |
| 81 | PathPrefix - prefixes a full path with \\?\ or \\?\UNC as | 96 | PathPrefix - prefixes a path with \\?\ or \\?\UNC if it doesn't |
| 82 | appropriate. | 97 | already have an extended prefix, is longer than MAX_PATH, |
| 98 | and is fully qualified. | ||
| 83 | ********************************************************************/ | 99 | ********************************************************************/ |
| 84 | DAPI_(HRESULT) PathPrefix( | 100 | DAPI_(HRESULT) PathPrefix( |
| 85 | __inout LPWSTR *psczFullPath | 101 | __inout_z LPWSTR *psczFullPath, |
| 102 | __in SIZE_T cchFullPath, | ||
| 103 | __in DWORD dwPrefixFlags | ||
| 86 | ); | 104 | ); |
| 87 | 105 | ||
| 88 | /******************************************************************* | 106 | /******************************************************************* |
| @@ -204,14 +222,27 @@ DAPI_(HRESULT) PathGetKnownFolder( | |||
| 204 | ); | 222 | ); |
| 205 | 223 | ||
| 206 | /******************************************************************* | 224 | /******************************************************************* |
| 225 | PathSkipPastRoot - returns a pointer to the first character after | ||
| 226 | the root portion of the path or NULL if the path has no root. | ||
| 227 | For example, the pointer will point to the "a" in "after": | ||
| 228 | C:\after, C:after, \after, \\server\share\after, | ||
| 229 | \\?\C:\afterroot, \\?\UNC\server\share\after | ||
| 230 | *******************************************************************/ | ||
| 231 | DAPI_(LPCWSTR) PathSkipPastRoot( | ||
| 232 | __in_z LPCWSTR wzPath, | ||
| 233 | __out_opt BOOL* pfHasExtendedPrefix, | ||
| 234 | __out_opt BOOL* pfFullyQualified, | ||
| 235 | __out_opt BOOL* pfUNC | ||
| 236 | ); | ||
| 237 | |||
| 238 | /******************************************************************* | ||
| 207 | PathIsFullyQualified - returns true if the path is fully qualified; false otherwise. | 239 | PathIsFullyQualified - returns true if the path is fully qualified; false otherwise. |
| 208 | Note that some rooted paths like C:dir are not fully qualified. | 240 | Note that some rooted paths like C:dir are not fully qualified. |
| 209 | For example, these are all fully qualified: C:\dir, C:/dir, \\server\share, \\?\C:\dir. | 241 | For example, these are all fully qualified: C:\dir, C:/dir, \\server\share, \\?\C:\dir. |
| 210 | For example, these are not fully qualified: C:dir, C:, \dir, dir, dir\subdir. | 242 | For example, these are not fully qualified: C:dir, C:, \dir, dir, dir\subdir. |
| 211 | *******************************************************************/ | 243 | *******************************************************************/ |
| 212 | DAPI_(BOOL) PathIsFullyQualified( | 244 | DAPI_(BOOL) PathIsFullyQualified( |
| 213 | __in_z LPCWSTR wzPath, | 245 | __in_z LPCWSTR wzPath |
| 214 | __out_opt BOOL* pfHasLongPathPrefix | ||
| 215 | ); | 246 | ); |
| 216 | 247 | ||
| 217 | /******************************************************************* | 248 | /******************************************************************* |
diff --git a/src/libs/dutil/WixToolset.DUtil/logutil.cpp b/src/libs/dutil/WixToolset.DUtil/logutil.cpp index 94c21374..88a90d8c 100644 --- a/src/libs/dutil/WixToolset.DUtil/logutil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/logutil.cpp | |||
| @@ -133,7 +133,7 @@ extern "C" HRESULT DAPI LogOpen( | |||
| 133 | hr = PathConcat(wzDirectory, wzLog, &sczCombined); | 133 | hr = PathConcat(wzDirectory, wzLog, &sczCombined); |
| 134 | LoguExitOnFailure(hr, "Failed to combine the log path."); | 134 | LoguExitOnFailure(hr, "Failed to combine the log path."); |
| 135 | 135 | ||
| 136 | if (!PathIsFullyQualified(sczCombined, NULL)) | 136 | if (!PathIsFullyQualified(sczCombined)) |
| 137 | { | 137 | { |
| 138 | hr = PathExpand(&LogUtil_sczLogPath, sczCombined, PATH_EXPAND_FULLPATH); | 138 | hr = PathExpand(&LogUtil_sczLogPath, sczCombined, PATH_EXPAND_FULLPATH); |
| 139 | LoguExitOnFailure(hr, "Failed to expand the log path."); | 139 | LoguExitOnFailure(hr, "Failed to expand the log path."); |
diff --git a/src/libs/dutil/WixToolset.DUtil/path2utl.cpp b/src/libs/dutil/WixToolset.DUtil/path2utl.cpp index 1957a8c5..9e48f9d5 100644 --- a/src/libs/dutil/WixToolset.DUtil/path2utl.cpp +++ b/src/libs/dutil/WixToolset.DUtil/path2utl.cpp | |||
| @@ -64,31 +64,12 @@ DAPI_(HRESULT) PathCanonicalizeForComparison( | |||
| 64 | 64 | ||
| 65 | if (PATH_CANONICALIZE_KEEP_UNC_ROOT & dwCanonicalizeFlags) | 65 | if (PATH_CANONICALIZE_KEEP_UNC_ROOT & dwCanonicalizeFlags) |
| 66 | { | 66 | { |
| 67 | if (L'\\' == sczNormalizedPath[0] && (L'\\' == sczNormalizedPath[1] || L'?' == sczNormalizedPath[1]) && L'?' == sczNormalizedPath[2] && L'\\' == sczNormalizedPath[3]) | 67 | BOOL fUNC = FALSE; |
| 68 | LPCWSTR wzPastRoot = PathSkipPastRoot(sczNormalizedPath, NULL, NULL, &fUNC); | ||
| 69 | if (fUNC) | ||
| 68 | { | 70 | { |
| 69 | if (L'U' == sczNormalizedPath[4] && L'N' == sczNormalizedPath[5] && L'C' == sczNormalizedPath[6] && L'\\' == sczNormalizedPath[7]) | 71 | wzNormalizedPath = wzPastRoot; |
| 70 | { | 72 | cchUncRootLength = wzPastRoot - sczNormalizedPath; |
| 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 | } | 73 | } |
| 93 | } | 74 | } |
| 94 | 75 | ||
| @@ -115,22 +96,24 @@ DAPI_(HRESULT) PathCanonicalizeForComparison( | |||
| 115 | if (PATH_CANONICALIZE_BACKSLASH_TERMINATE & dwCanonicalizeFlags) | 96 | if (PATH_CANONICALIZE_BACKSLASH_TERMINATE & dwCanonicalizeFlags) |
| 116 | { | 97 | { |
| 117 | hr = PathBackslashTerminate(psczCanonicalized); | 98 | hr = PathBackslashTerminate(psczCanonicalized); |
| 118 | PathExitOnFailure(hr, "Failed to backslash terminate the canonicalized path"); | 99 | PathExitOnFailure(hr, "Failed to backslash terminate the canonicalized path."); |
| 119 | } | 100 | } |
| 120 | 101 | ||
| 121 | if (PathIsFullyQualified(*psczCanonicalized, &fHasPrefix) && !fHasPrefix && | 102 | if (PATH_CANONICALIZE_APPEND_EXTENDED_PATH_PREFIX & dwCanonicalizeFlags) |
| 122 | (PATH_CANONICALIZE_APPEND_LONG_PATH_PREFIX & dwCanonicalizeFlags)) | ||
| 123 | { | 103 | { |
| 124 | hr = PathPrefix(psczCanonicalized); | 104 | hr = PathPrefix(psczCanonicalized, 0, PATH_PREFIX_SHORT_PATHS); |
| 125 | PathExitOnFailure(hr, "Failed to ensure the long path prefix on the canonicalized path"); | 105 | PathExitOnFailure(hr, "Failed to ensure the extended path prefix on the canonicalized path."); |
| 126 | |||
| 127 | fHasPrefix = TRUE; | ||
| 128 | } | 106 | } |
| 129 | 107 | ||
| 108 | PathSkipPastRoot(*psczCanonicalized, &fHasPrefix, NULL, NULL); | ||
| 109 | |||
| 130 | if (fHasPrefix) | 110 | if (fHasPrefix) |
| 131 | { | 111 | { |
| 132 | // Canonicalize \??\ into \\?\. | 112 | // Canonicalize prefix into \\?\. |
| 113 | (*psczCanonicalized)[0] = L'\\'; | ||
| 133 | (*psczCanonicalized)[1] = L'\\'; | 114 | (*psczCanonicalized)[1] = L'\\'; |
| 115 | (*psczCanonicalized)[2] = L'?'; | ||
| 116 | (*psczCanonicalized)[3] = L'\\'; | ||
| 134 | } | 117 | } |
| 135 | 118 | ||
| 136 | LExit: | 119 | LExit: |
| @@ -188,7 +171,7 @@ DAPI_(HRESULT) PathCompareCanonicalized( | |||
| 188 | HRESULT hr = S_OK; | 171 | HRESULT hr = S_OK; |
| 189 | LPWSTR sczCanonicalized1 = NULL; | 172 | LPWSTR sczCanonicalized1 = NULL; |
| 190 | LPWSTR sczCanonicalized2 = NULL; | 173 | LPWSTR sczCanonicalized2 = NULL; |
| 191 | DWORD dwDefaultFlags = PATH_CANONICALIZE_APPEND_LONG_PATH_PREFIX | PATH_CANONICALIZE_KEEP_UNC_ROOT; | 174 | DWORD dwDefaultFlags = PATH_CANONICALIZE_APPEND_EXTENDED_PATH_PREFIX | PATH_CANONICALIZE_KEEP_UNC_ROOT; |
| 192 | int nResult = 0; | 175 | int nResult = 0; |
| 193 | 176 | ||
| 194 | if (!wzPath1 || !wzPath2) | 177 | if (!wzPath1 || !wzPath2) |
| @@ -221,7 +204,7 @@ DAPI_(HRESULT) PathDirectoryContainsPath( | |||
| 221 | HRESULT hr = S_OK; | 204 | HRESULT hr = S_OK; |
| 222 | LPWSTR sczCanonicalizedDirectory = NULL; | 205 | LPWSTR sczCanonicalizedDirectory = NULL; |
| 223 | LPWSTR sczCanonicalizedPath = NULL; | 206 | LPWSTR sczCanonicalizedPath = NULL; |
| 224 | DWORD dwDefaultFlags = PATH_CANONICALIZE_APPEND_LONG_PATH_PREFIX | PATH_CANONICALIZE_KEEP_UNC_ROOT; | 207 | DWORD dwDefaultFlags = PATH_CANONICALIZE_APPEND_EXTENDED_PATH_PREFIX | PATH_CANONICALIZE_KEEP_UNC_ROOT; |
| 225 | size_t cchDirectory = 0; | 208 | size_t cchDirectory = 0; |
| 226 | 209 | ||
| 227 | if (!wzDirectory || !*wzDirectory) | 210 | if (!wzDirectory || !*wzDirectory) |
| @@ -239,7 +222,7 @@ DAPI_(HRESULT) PathDirectoryContainsPath( | |||
| 239 | hr = PathCanonicalizeForComparison(wzPath, dwDefaultFlags, &sczCanonicalizedPath); | 222 | hr = PathCanonicalizeForComparison(wzPath, dwDefaultFlags, &sczCanonicalizedPath); |
| 240 | PathExitOnFailure(hr, "Failed to canonicalize the path."); | 223 | PathExitOnFailure(hr, "Failed to canonicalize the path."); |
| 241 | 224 | ||
| 242 | if (!PathIsFullyQualified(sczCanonicalizedDirectory, NULL)) | 225 | if (!PathIsFullyQualified(sczCanonicalizedDirectory)) |
| 243 | { | 226 | { |
| 244 | PathExitWithRootFailure(hr, E_INVALIDARG, "wzDirectory must be a fully qualified path."); | 227 | PathExitWithRootFailure(hr, E_INVALIDARG, "wzDirectory must be a fully qualified path."); |
| 245 | } | 228 | } |
diff --git a/src/libs/dutil/WixToolset.DUtil/pathutil.cpp b/src/libs/dutil/WixToolset.DUtil/pathutil.cpp index abbf4d4b..1ac76626 100644 --- a/src/libs/dutil/WixToolset.DUtil/pathutil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/pathutil.cpp | |||
| @@ -120,13 +120,34 @@ LExit: | |||
| 120 | 120 | ||
| 121 | DAPI_(HRESULT) PathGetParentPath( | 121 | DAPI_(HRESULT) PathGetParentPath( |
| 122 | __in_z LPCWSTR wzPath, | 122 | __in_z LPCWSTR wzPath, |
| 123 | __out_z LPWSTR *psczParent | 123 | __out_z LPWSTR* psczParent, |
| 124 | __out_opt SIZE_T* pcchRoot | ||
| 124 | ) | 125 | ) |
| 125 | { | 126 | { |
| 126 | HRESULT hr = S_OK; | 127 | HRESULT hr = S_OK; |
| 128 | LPCWSTR wzPastRoot = NULL; | ||
| 127 | LPCWSTR wzParent = NULL; | 129 | LPCWSTR wzParent = NULL; |
| 130 | LPCWSTR wz = NULL; | ||
| 128 | 131 | ||
| 129 | for (LPCWSTR wz = wzPath; *wz; ++wz) | 132 | wzPastRoot = PathSkipPastRoot(wzPath, NULL, NULL, NULL); |
| 133 | |||
| 134 | if (pcchRoot) | ||
| 135 | { | ||
| 136 | *pcchRoot = !wzPastRoot ? 0 : wzPastRoot - wzPath; | ||
| 137 | } | ||
| 138 | |||
| 139 | if (wzPastRoot && *wzPastRoot) | ||
| 140 | { | ||
| 141 | Assert(wzPastRoot > wzPath); | ||
| 142 | wz = wzPastRoot; | ||
| 143 | wzParent = wzPastRoot - 1; | ||
| 144 | } | ||
| 145 | else | ||
| 146 | { | ||
| 147 | wz = wzPath; | ||
| 148 | } | ||
| 149 | |||
| 150 | for (; *wz; ++wz) | ||
| 130 | { | 151 | { |
| 131 | if (IsPathSeparatorChar(*wz) && wz[1]) | 152 | if (IsPathSeparatorChar(*wz) && wz[1]) |
| 132 | { | 153 | { |
| @@ -143,7 +164,7 @@ DAPI_(HRESULT) PathGetParentPath( | |||
| 143 | } | 164 | } |
| 144 | else | 165 | else |
| 145 | { | 166 | { |
| 146 | ReleaseNullStr(psczParent); | 167 | ReleaseNullStr(*psczParent); |
| 147 | } | 168 | } |
| 148 | 169 | ||
| 149 | LExit: | 170 | LExit: |
| @@ -164,9 +185,8 @@ DAPI_(HRESULT) PathExpand( | |||
| 164 | LPWSTR sczExpandedPath = NULL; | 185 | LPWSTR sczExpandedPath = NULL; |
| 165 | SIZE_T cchWritten = 0; | 186 | SIZE_T cchWritten = 0; |
| 166 | DWORD cchExpandedPath = 0; | 187 | DWORD cchExpandedPath = 0; |
| 167 | SIZE_T cbSize = 0; | ||
| 168 | |||
| 169 | LPWSTR sczFullPath = NULL; | 188 | LPWSTR sczFullPath = NULL; |
| 189 | DWORD dwPrefixFlags = 0; | ||
| 170 | 190 | ||
| 171 | // | 191 | // |
| 172 | // First, expand any environment variables. | 192 | // First, expand any environment variables. |
| @@ -201,20 +221,7 @@ DAPI_(HRESULT) PathExpand( | |||
| 201 | } | 221 | } |
| 202 | } | 222 | } |
| 203 | 223 | ||
| 204 | if (MAX_PATH < cch) | 224 | cchWritten = cch; |
| 205 | { | ||
| 206 | hr = PathPrefix(&sczExpandedPath); // ignore invald arg from path prefix because this may not be a complete path yet | ||
| 207 | if (E_INVALIDARG == hr) | ||
| 208 | { | ||
| 209 | hr = S_OK; | ||
| 210 | } | ||
| 211 | PathExitOnFailure(hr, "Failed to prefix long path after expanding environment variables."); | ||
| 212 | |||
| 213 | hr = StrMaxLength(sczExpandedPath, &cbSize); | ||
| 214 | PathExitOnFailure(hr, "Failed to get max length of expanded path."); | ||
| 215 | |||
| 216 | cchExpandedPath = (DWORD)min(DWORD_MAX, cbSize); | ||
| 217 | } | ||
| 218 | } | 225 | } |
| 219 | 226 | ||
| 220 | // | 227 | // |
| @@ -227,11 +234,7 @@ DAPI_(HRESULT) PathExpand( | |||
| 227 | hr = PathGetFullPathName(wzPath, &sczFullPath, NULL, &cchWritten); | 234 | hr = PathGetFullPathName(wzPath, &sczFullPath, NULL, &cchWritten); |
| 228 | PathExitOnFailure(hr, "Failed to get full path for string: %ls", wzPath); | 235 | PathExitOnFailure(hr, "Failed to get full path for string: %ls", wzPath); |
| 229 | 236 | ||
| 230 | if (MAX_PATH < cchWritten) | 237 | dwPrefixFlags |= PATH_PREFIX_EXPECT_FULLY_QUALIFIED; |
| 231 | { | ||
| 232 | hr = PathPrefix(&sczFullPath); | ||
| 233 | PathExitOnFailure(hr, "Failed to prefix long path after expanding."); | ||
| 234 | } | ||
| 235 | } | 238 | } |
| 236 | else | 239 | else |
| 237 | { | 240 | { |
| @@ -239,6 +242,12 @@ DAPI_(HRESULT) PathExpand( | |||
| 239 | sczExpandedPath = NULL; | 242 | sczExpandedPath = NULL; |
| 240 | } | 243 | } |
| 241 | 244 | ||
| 245 | if (dwResolveFlags) | ||
| 246 | { | ||
| 247 | hr = PathPrefix(&sczFullPath, cchWritten, dwPrefixFlags); | ||
| 248 | PathExitOnFailure(hr, "Failed to prefix path after expanding."); | ||
| 249 | } | ||
| 250 | |||
| 242 | hr = StrAllocString(psczFullPath, sczFullPath ? sczFullPath : wzRelativePath, 0); | 251 | hr = StrAllocString(psczFullPath, sczFullPath ? sczFullPath : wzRelativePath, 0); |
| 243 | PathExitOnFailure(hr, "Failed to copy relative path into full path."); | 252 | PathExitOnFailure(hr, "Failed to copy relative path into full path."); |
| 244 | 253 | ||
| @@ -319,29 +328,54 @@ LExit: | |||
| 319 | 328 | ||
| 320 | 329 | ||
| 321 | DAPI_(HRESULT) PathPrefix( | 330 | DAPI_(HRESULT) PathPrefix( |
| 322 | __inout LPWSTR *psczFullPath | 331 | __inout_z LPWSTR* psczFullPath, |
| 332 | __in SIZE_T cchFullPath, | ||
| 333 | __in DWORD dwPrefixFlags | ||
| 323 | ) | 334 | ) |
| 324 | { | 335 | { |
| 325 | Assert(psczFullPath && *psczFullPath); | 336 | Assert(psczFullPath); |
| 326 | 337 | ||
| 327 | HRESULT hr = S_OK; | 338 | HRESULT hr = S_OK; |
| 328 | LPWSTR wzFullPath = *psczFullPath; | 339 | LPWSTR wzFullPath = *psczFullPath; |
| 329 | BOOL fFullyQualified = FALSE; | 340 | BOOL fFullyQualified = FALSE; |
| 330 | BOOL fHasPrefix = FALSE; | 341 | BOOL fHasPrefix = FALSE; |
| 342 | BOOL fUNC = FALSE; | ||
| 331 | SIZE_T cbFullPath = 0; | 343 | SIZE_T cbFullPath = 0; |
| 332 | 344 | ||
| 333 | fFullyQualified = PathIsFullyQualified(wzFullPath, &fHasPrefix); | 345 | PathSkipPastRoot(wzFullPath, &fHasPrefix, &fFullyQualified, &fUNC); |
| 346 | |||
| 334 | if (fHasPrefix) | 347 | if (fHasPrefix) |
| 335 | { | 348 | { |
| 336 | ExitFunction(); | 349 | ExitFunction(); |
| 337 | } | 350 | } |
| 338 | 351 | ||
| 339 | if (fFullyQualified && L':' == wzFullPath[1]) // normal path | 352 | // The prefix is only allowed on fully qualified paths. |
| 353 | if (!fFullyQualified) | ||
| 340 | { | 354 | { |
| 341 | hr = StrAllocPrefix(psczFullPath, L"\\\\?\\", 4); | 355 | if (dwPrefixFlags & PATH_PREFIX_EXPECT_FULLY_QUALIFIED) |
| 342 | PathExitOnFailure(hr, "Failed to add prefix to file path."); | 356 | { |
| 357 | PathExitWithRootFailure(hr, E_INVALIDARG, "Expected fully qualified path provided to prefix: %ls.", wzFullPath); | ||
| 358 | } | ||
| 359 | |||
| 360 | ExitFunction(); | ||
| 361 | } | ||
| 362 | |||
| 363 | if (!(dwPrefixFlags & PATH_PREFIX_SHORT_PATHS)) | ||
| 364 | { | ||
| 365 | // The prefix is not necessary unless the path is longer than MAX_PATH. | ||
| 366 | if (!cchFullPath) | ||
| 367 | { | ||
| 368 | hr = ::StringCchLengthW(wzFullPath, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cchFullPath)); | ||
| 369 | PathExitOnFailure(hr, "Failed to get length of path to prefix."); | ||
| 370 | } | ||
| 371 | |||
| 372 | if (MAX_PATH >= cchFullPath) | ||
| 373 | { | ||
| 374 | ExitFunction(); | ||
| 375 | } | ||
| 343 | } | 376 | } |
| 344 | else if (fFullyQualified && IsPathSeparatorChar(wzFullPath[1])) // UNC | 377 | |
| 378 | if (fUNC) | ||
| 345 | { | 379 | { |
| 346 | hr = StrSize(*psczFullPath, &cbFullPath); | 380 | hr = StrSize(*psczFullPath, &cbFullPath); |
| 347 | PathExitOnFailure(hr, "Failed to get size of full path."); | 381 | PathExitOnFailure(hr, "Failed to get size of full path."); |
| @@ -352,10 +386,10 @@ DAPI_(HRESULT) PathPrefix( | |||
| 352 | hr = StrAllocPrefix(psczFullPath, L"\\\\?\\UNC", 7); | 386 | hr = StrAllocPrefix(psczFullPath, L"\\\\?\\UNC", 7); |
| 353 | PathExitOnFailure(hr, "Failed to add prefix to UNC path."); | 387 | PathExitOnFailure(hr, "Failed to add prefix to UNC path."); |
| 354 | } | 388 | } |
| 355 | else | 389 | else // must be a normal path |
| 356 | { | 390 | { |
| 357 | hr = E_INVALIDARG; | 391 | hr = StrAllocPrefix(psczFullPath, L"\\\\?\\", 4); |
| 358 | PathExitOnFailure(hr, "Invalid path provided to prefix: %ls.", wzFullPath); | 392 | PathExitOnFailure(hr, "Failed to add prefix to file path."); |
| 359 | } | 393 | } |
| 360 | 394 | ||
| 361 | LExit: | 395 | LExit: |
| @@ -970,55 +1004,114 @@ LExit: | |||
| 970 | } | 1004 | } |
| 971 | 1005 | ||
| 972 | 1006 | ||
| 973 | DAPI_(BOOL) PathIsFullyQualified( | 1007 | DAPI_(LPCWSTR) PathSkipPastRoot( |
| 974 | __in_z LPCWSTR wzPath, | 1008 | __in_z_opt LPCWSTR wzPath, |
| 975 | __out_opt BOOL* pfHasLongPathPrefix | 1009 | __out_opt BOOL* pfHasExtendedPrefix, |
| 1010 | __out_opt BOOL* pfFullyQualified, | ||
| 1011 | __out_opt BOOL* pfUNC | ||
| 976 | ) | 1012 | ) |
| 977 | { | 1013 | { |
| 1014 | LPCWSTR wzPastRoot = NULL; | ||
| 1015 | BOOL fHasPrefix = FALSE; | ||
| 978 | BOOL fFullyQualified = FALSE; | 1016 | BOOL fFullyQualified = FALSE; |
| 979 | BOOL fHasLongPathPrefix = FALSE; | 1017 | BOOL fUNC = FALSE; |
| 1018 | DWORD dwRootMissingSlashes = 0; | ||
| 980 | 1019 | ||
| 981 | if (!wzPath || !wzPath[0] || !wzPath[1]) | 1020 | if (!wzPath || !*wzPath) |
| 982 | { | 1021 | { |
| 983 | // There is no way to specify a fully qualified path with one character (or less). | ||
| 984 | ExitFunction(); | 1022 | ExitFunction(); |
| 985 | } | 1023 | } |
| 986 | 1024 | ||
| 987 | if (!IsPathSeparatorChar(wzPath[0])) | 1025 | if (IsPathSeparatorChar(wzPath[0])) |
| 988 | { | 1026 | { |
| 989 | // The only way to specify a fully qualified path that doesn't begin with a slash | 1027 | if (IsPathSeparatorChar(wzPath[1]) && (L'?' == wzPath[2] || L'.' == wzPath[2]) && IsPathSeparatorChar(wzPath[3]) || |
| 990 | // is the drive, colon, slash format (C:\). | 1028 | L'?' == wzPath[1] && L'?' == wzPath[2] && IsPathSeparatorChar(wzPath[3])) |
| 991 | if (IsValidDriveChar(wzPath[0]) && | ||
| 992 | L':' == wzPath[1] && | ||
| 993 | IsPathSeparatorChar(wzPath[2])) | ||
| 994 | { | 1029 | { |
| 995 | fFullyQualified = TRUE; | 1030 | fHasPrefix = TRUE; |
| 996 | } | ||
| 997 | 1031 | ||
| 998 | ExitFunction(); | 1032 | if (L'U' == wzPath[4] && L'N' == wzPath[5] && L'C' == wzPath[6] && IsPathSeparatorChar(wzPath[7])) |
| 1033 | { | ||
| 1034 | fUNC = TRUE; | ||
| 1035 | wzPastRoot = wzPath + 8; | ||
| 1036 | dwRootMissingSlashes = 2; | ||
| 1037 | } | ||
| 1038 | else | ||
| 1039 | { | ||
| 1040 | wzPastRoot = wzPath + 4; | ||
| 1041 | dwRootMissingSlashes = 1; | ||
| 1042 | } | ||
| 1043 | } | ||
| 1044 | else if (IsPathSeparatorChar(wzPath[1])) | ||
| 1045 | { | ||
| 1046 | fUNC = TRUE; | ||
| 1047 | wzPastRoot = wzPath + 2; | ||
| 1048 | dwRootMissingSlashes = 2; | ||
| 1049 | } | ||
| 999 | } | 1050 | } |
| 1000 | 1051 | ||
| 1001 | // Non-drive fully qualified paths must start with \\ or \?. | 1052 | if (dwRootMissingSlashes) |
| 1002 | // \??\ is an archaic form of \\?\. | ||
| 1003 | if (L'?' != wzPath[1] && !IsPathSeparatorChar(wzPath[1])) | ||
| 1004 | { | 1053 | { |
| 1005 | ExitFunction(); | 1054 | Assert(wzPastRoot); |
| 1055 | fFullyQualified = TRUE; | ||
| 1056 | |||
| 1057 | for (; *wzPastRoot && dwRootMissingSlashes; ++wzPastRoot) | ||
| 1058 | { | ||
| 1059 | if (IsPathSeparatorChar(*wzPastRoot)) | ||
| 1060 | { | ||
| 1061 | --dwRootMissingSlashes; | ||
| 1062 | } | ||
| 1063 | } | ||
| 1006 | } | 1064 | } |
| 1065 | else | ||
| 1066 | { | ||
| 1067 | Assert(!wzPastRoot); | ||
| 1007 | 1068 | ||
| 1008 | fFullyQualified = TRUE; | 1069 | if (IsPathSeparatorChar(wzPath[0])) |
| 1070 | { | ||
| 1071 | wzPastRoot = wzPath + 1; | ||
| 1072 | } | ||
| 1073 | else if (IsValidDriveChar(wzPath[0]) && wzPath[1] == L':') | ||
| 1074 | { | ||
| 1075 | if (IsPathSeparatorChar(wzPath[2])) | ||
| 1076 | { | ||
| 1077 | fFullyQualified = TRUE; | ||
| 1078 | wzPastRoot = wzPath + 3; | ||
| 1079 | } | ||
| 1080 | else | ||
| 1081 | { | ||
| 1082 | wzPastRoot = wzPath + 2; | ||
| 1083 | } | ||
| 1084 | } | ||
| 1085 | } | ||
| 1009 | 1086 | ||
| 1010 | if (L'?' == wzPath[2] && IsPathSeparatorChar(wzPath[3])) | 1087 | LExit: |
| 1088 | if (pfHasExtendedPrefix) | ||
| 1011 | { | 1089 | { |
| 1012 | fHasLongPathPrefix = TRUE; | 1090 | *pfHasExtendedPrefix = fHasPrefix; |
| 1013 | } | 1091 | } |
| 1014 | 1092 | ||
| 1093 | if (pfFullyQualified) | ||
| 1094 | { | ||
| 1095 | *pfFullyQualified = fFullyQualified; | ||
| 1096 | } | ||
| 1015 | 1097 | ||
| 1016 | LExit: | 1098 | if (pfUNC) |
| 1017 | if (pfHasLongPathPrefix) | ||
| 1018 | { | 1099 | { |
| 1019 | *pfHasLongPathPrefix = fHasLongPathPrefix; | 1100 | *pfUNC = fUNC; |
| 1020 | } | 1101 | } |
| 1021 | 1102 | ||
| 1103 | return wzPastRoot; | ||
| 1104 | } | ||
| 1105 | |||
| 1106 | |||
| 1107 | DAPI_(BOOL) PathIsFullyQualified( | ||
| 1108 | __in_z LPCWSTR wzPath | ||
| 1109 | ) | ||
| 1110 | { | ||
| 1111 | BOOL fFullyQualified = FALSE; | ||
| 1112 | |||
| 1113 | PathSkipPastRoot(wzPath, NULL, &fFullyQualified, NULL); | ||
| 1114 | |||
| 1022 | return fFullyQualified; | 1115 | return fFullyQualified; |
| 1023 | } | 1116 | } |
| 1024 | 1117 | ||
| @@ -1027,9 +1120,7 @@ DAPI_(BOOL) PathIsRooted( | |||
| 1027 | __in_z LPCWSTR wzPath | 1120 | __in_z LPCWSTR wzPath |
| 1028 | ) | 1121 | ) |
| 1029 | { | 1122 | { |
| 1030 | return wzPath && | 1123 | return NULL != PathSkipPastRoot(wzPath, NULL, NULL, NULL); |
| 1031 | (IsPathSeparatorChar(wzPath[0]) || | ||
| 1032 | IsValidDriveChar(wzPath[0]) && wzPath[1] == L':'); | ||
| 1033 | } | 1124 | } |
| 1034 | 1125 | ||
| 1035 | 1126 | ||
| @@ -1118,78 +1209,47 @@ DAPI_(HRESULT) PathGetHierarchyArray( | |||
| 1118 | ) | 1209 | ) |
| 1119 | { | 1210 | { |
| 1120 | HRESULT hr = S_OK; | 1211 | HRESULT hr = S_OK; |
| 1121 | LPWSTR sczPathCopy = NULL; | 1212 | LPCWSTR wz = NULL; |
| 1122 | LPWSTR sczNewPathCopy = NULL; | 1213 | SIZE_T cch = 0; |
| 1123 | DWORD cArraySpacesNeeded = 0; | 1214 | *pcPathArray = 0; |
| 1124 | size_t cchPath = 0; | ||
| 1125 | 1215 | ||
| 1126 | hr = ::StringCchLengthW(wzPath, STRSAFE_MAX_LENGTH, &cchPath); | 1216 | PathExitOnNull(wzPath, hr, E_INVALIDARG, "wzPath is required."); |
| 1127 | PathExitOnRootFailure(hr, "Failed to get string length of path: %ls", wzPath); | ||
| 1128 | 1217 | ||
| 1129 | if (!cchPath) | 1218 | wz = PathSkipPastRoot(wzPath, NULL, NULL, NULL); |
| 1219 | if (wz) | ||
| 1130 | { | 1220 | { |
| 1131 | ExitFunction1(hr = E_INVALIDARG); | 1221 | cch = wz - wzPath; |
| 1132 | } | ||
| 1133 | 1222 | ||
| 1134 | for (size_t i = 0; i < cchPath; ++i) | 1223 | hr = MemEnsureArraySize(reinterpret_cast<void**>(prgsczPathArray), 1, sizeof(LPWSTR), 5); |
| 1135 | { | 1224 | PathExitOnFailure(hr, "Failed to allocate array."); |
| 1136 | if (IsPathSeparatorChar(wzPath[i])) | ||
| 1137 | { | ||
| 1138 | ++cArraySpacesNeeded; | ||
| 1139 | } | ||
| 1140 | } | ||
| 1141 | 1225 | ||
| 1142 | if (!IsPathSeparatorChar(wzPath[cchPath - 1])) | 1226 | hr = StrAllocString(*prgsczPathArray, wzPath, cch); |
| 1143 | { | 1227 | PathExitOnFailure(hr, "Failed to copy root into array."); |
| 1144 | ++cArraySpacesNeeded; | ||
| 1145 | } | ||
| 1146 | 1228 | ||
| 1147 | // 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. | 1229 | *pcPathArray += 1; |
| 1148 | if (IsPathSeparatorChar(wzPath[0]) && IsPathSeparatorChar(wzPath[1])) | 1230 | } |
| 1231 | else | ||
| 1149 | { | 1232 | { |
| 1150 | if (3 > cArraySpacesNeeded) | 1233 | wz = wzPath; |
| 1151 | { | ||
| 1152 | ExitFunction1(hr = E_INVALIDARG); | ||
| 1153 | } | ||
| 1154 | |||
| 1155 | cArraySpacesNeeded -= 3; | ||
| 1156 | } | 1234 | } |
| 1157 | 1235 | ||
| 1158 | Assert(cArraySpacesNeeded >= 1); | 1236 | for (; *wz; ++wz) |
| 1159 | |||
| 1160 | hr = MemEnsureArraySize(reinterpret_cast<void **>(prgsczPathArray), cArraySpacesNeeded, sizeof(LPWSTR), 0); | ||
| 1161 | PathExitOnFailure(hr, "Failed to allocate array of size %u for parent directories", cArraySpacesNeeded); | ||
| 1162 | *pcPathArray = cArraySpacesNeeded; | ||
| 1163 | |||
| 1164 | hr = StrAllocString(&sczPathCopy, wzPath, 0); | ||
| 1165 | PathExitOnFailure(hr, "Failed to allocate copy of original path"); | ||
| 1166 | |||
| 1167 | for (DWORD i = 0; i < cArraySpacesNeeded; ++i) | ||
| 1168 | { | 1237 | { |
| 1169 | hr = StrAllocString((*prgsczPathArray) + cArraySpacesNeeded - 1 - i, sczPathCopy, 0); | 1238 | ++cch; |
| 1170 | PathExitOnFailure(hr, "Failed to copy path"); | ||
| 1171 | 1239 | ||
| 1172 | DWORD cchPathCopy = lstrlenW(sczPathCopy); | 1240 | if (IsPathSeparatorChar(*wz) || !wz[1]) |
| 1173 | |||
| 1174 | // 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 | ||
| 1175 | if (IsPathSeparatorChar(wzPath[cchPathCopy - 1])) | ||
| 1176 | { | 1241 | { |
| 1177 | sczPathCopy[cchPathCopy - 1] = L'\0'; | 1242 | hr = MemEnsureArraySizeForNewItems(reinterpret_cast<void**>(prgsczPathArray), *pcPathArray, 1, sizeof(LPWSTR), 5); |
| 1178 | } | 1243 | PathExitOnFailure(hr, "Failed to allocate array."); |
| 1179 | |||
| 1180 | hr = PathGetDirectory(sczPathCopy, &sczNewPathCopy); | ||
| 1181 | PathExitOnFailure(hr, "Failed to get directory portion of path"); | ||
| 1182 | 1244 | ||
| 1183 | ReleaseStr(sczPathCopy); | 1245 | hr = StrAllocString(*prgsczPathArray + *pcPathArray, wzPath, cch); |
| 1184 | sczPathCopy = sczNewPathCopy; | 1246 | PathExitOnFailure(hr, "Failed to copy path into array."); |
| 1185 | sczNewPathCopy = NULL; | ||
| 1186 | } | ||
| 1187 | 1247 | ||
| 1188 | hr = S_OK; | 1248 | *pcPathArray += 1; |
| 1249 | } | ||
| 1250 | } | ||
| 1189 | 1251 | ||
| 1190 | LExit: | 1252 | LExit: |
| 1191 | ReleaseStr(sczPathCopy); | ||
| 1192 | |||
| 1193 | return hr; | 1253 | return hr; |
| 1194 | } | 1254 | } |
| 1195 | 1255 | ||
diff --git a/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp index 2505c6bf..d1d304d3 100644 --- a/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp +++ b/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp | |||
| @@ -135,14 +135,22 @@ namespace DutilTests | |||
| 135 | NativeAssert::Succeeded(hr, "Failed to canonicalize path"); | 135 | NativeAssert::Succeeded(hr, "Failed to canonicalize path"); |
| 136 | NativeAssert::StringEqual(L"\\\\server\\share\\", sczCanonicalized); | 136 | NativeAssert::StringEqual(L"\\\\server\\share\\", sczCanonicalized); |
| 137 | 137 | ||
| 138 | hr = PathCanonicalizeForComparison(L"\\\\.\\share\\otherdir\\unc.exe", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized); | 138 | hr = PathCanonicalizeForComparison(L"\\\\.\\UNC\\server\\share\\..\\unc.exe", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized); |
| 139 | NativeAssert::Succeeded(hr, "Failed to canonicalize path"); | 139 | NativeAssert::Succeeded(hr, "Failed to canonicalize path"); |
| 140 | NativeAssert::StringEqual(L"\\\\.\\share\\otherdir\\unc.exe", sczCanonicalized); | 140 | NativeAssert::StringEqual(L"\\\\?\\UNC\\server\\share\\unc.exe", sczCanonicalized); |
| 141 | 141 | ||
| 142 | hr = PathCanonicalizeForComparison(L"\\\\.\\share\\otherdir\\unc.exe", 0, &sczCanonicalized); | 142 | hr = PathCanonicalizeForComparison(L"\\\\..\\share\\otherdir\\unc.exe", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized); |
| 143 | NativeAssert::Succeeded(hr, "Failed to canonicalize path"); | ||
| 144 | NativeAssert::StringEqual(L"\\\\..\\share\\otherdir\\unc.exe", sczCanonicalized); | ||
| 145 | |||
| 146 | hr = PathCanonicalizeForComparison(L"\\\\..\\share\\otherdir\\unc.exe", 0, &sczCanonicalized); | ||
| 143 | NativeAssert::Succeeded(hr, "Failed to canonicalize path"); | 147 | NativeAssert::Succeeded(hr, "Failed to canonicalize path"); |
| 144 | NativeAssert::StringEqual(L"\\\\share\\otherdir\\unc.exe", sczCanonicalized); | 148 | NativeAssert::StringEqual(L"\\\\share\\otherdir\\unc.exe", sczCanonicalized); |
| 145 | 149 | ||
| 150 | hr = PathCanonicalizeForComparison(L"\\\\.\\UNC\\share\\otherdir\\unc.exe", 0, &sczCanonicalized); | ||
| 151 | NativeAssert::Succeeded(hr, "Failed to canonicalize path"); | ||
| 152 | NativeAssert::StringEqual(L"\\\\UNC\\share\\otherdir\\unc.exe", sczCanonicalized); | ||
| 153 | |||
| 146 | hr = PathCanonicalizeForComparison(L"\\\\server\\share\\..\\..\\otherdir\\unc.exe", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized); | 154 | hr = PathCanonicalizeForComparison(L"\\\\server\\share\\..\\..\\otherdir\\unc.exe", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized); |
| 147 | NativeAssert::Succeeded(hr, "Failed to canonicalize path"); | 155 | NativeAssert::Succeeded(hr, "Failed to canonicalize path"); |
| 148 | NativeAssert::StringEqual(L"\\\\server\\share\\otherdir\\unc.exe", sczCanonicalized); | 156 | NativeAssert::StringEqual(L"\\\\server\\share\\otherdir\\unc.exe", sczCanonicalized); |
| @@ -215,7 +223,7 @@ namespace DutilTests | |||
| 215 | NativeAssert::Succeeded(hr, "Failed to canonicalize path"); | 223 | NativeAssert::Succeeded(hr, "Failed to canonicalize path"); |
| 216 | NativeAssert::StringEqual(L"C:\\invalid:pathchars?.exe", sczCanonicalized); | 224 | NativeAssert::StringEqual(L"C:\\invalid:pathchars?.exe", sczCanonicalized); |
| 217 | 225 | ||
| 218 | hr = PathCanonicalizeForComparison(L"C:\\addprefix.exe", PATH_CANONICALIZE_APPEND_LONG_PATH_PREFIX, &sczCanonicalized); | 226 | hr = PathCanonicalizeForComparison(L"C:\\addprefix.exe", PATH_CANONICALIZE_APPEND_EXTENDED_PATH_PREFIX, &sczCanonicalized); |
| 219 | NativeAssert::Succeeded(hr, "Failed to canonicalize path"); | 227 | NativeAssert::Succeeded(hr, "Failed to canonicalize path"); |
| 220 | NativeAssert::StringEqual(L"\\\\?\\C:\\addprefix.exe", sczCanonicalized); | 228 | NativeAssert::StringEqual(L"\\\\?\\C:\\addprefix.exe", sczCanonicalized); |
| 221 | 229 | ||
| @@ -312,7 +320,7 @@ namespace DutilTests | |||
| 312 | LPCWSTR rgwzPaths[8] = | 320 | LPCWSTR rgwzPaths[8] = |
| 313 | { | 321 | { |
| 314 | L"C:\\simplepath", L"D:\\simplepath", | 322 | L"C:\\simplepath", L"D:\\simplepath", |
| 315 | L"\\\\.\\share\\otherdir\\unc.exe", L"\\\\share\\otherdir\\unc.exe", | 323 | L"\\\\..\\share\\otherdir\\unc.exe", L"\\\\share\\otherdir\\unc.exe", |
| 316 | L"\\\\server\\.\\otherdir\\unc.exe", L"\\\\server\\otherdir\\unc.exe", | 324 | L"\\\\server\\.\\otherdir\\unc.exe", L"\\\\server\\otherdir\\unc.exe", |
| 317 | L"\\\\server\\\\otherdir\\unc.exe", L"\\\\server\\otherdir\\unc.exe", | 325 | L"\\\\server\\\\otherdir\\unc.exe", L"\\\\server\\otherdir\\unc.exe", |
| 318 | }; | 326 | }; |
| @@ -541,9 +549,10 @@ namespace DutilTests | |||
| 541 | { | 549 | { |
| 542 | HRESULT hr = S_OK; | 550 | HRESULT hr = S_OK; |
| 543 | LPWSTR sczPath = NULL; | 551 | LPWSTR sczPath = NULL; |
| 544 | LPCWSTR rgwzPaths[18] = | 552 | LPCWSTR rgwzPaths[20] = |
| 545 | { | 553 | { |
| 546 | L"C:\\a\\b", L"C:\\a\\", | 554 | L"C:\\a\\b", L"C:\\a\\", |
| 555 | L"C:\\a\\b\\", L"C:\\a\\b\\", | ||
| 547 | L"C:\\a", L"C:\\", | 556 | L"C:\\a", L"C:\\", |
| 548 | L"C:\\", L"C:\\", | 557 | L"C:\\", L"C:\\", |
| 549 | L"\"C:\\a\\b\\c\"", L"\"C:\\a\\b\\", | 558 | L"\"C:\\a\\b\\c\"", L"\"C:\\a\\b\\", |
| @@ -570,6 +579,40 @@ namespace DutilTests | |||
| 570 | } | 579 | } |
| 571 | 580 | ||
| 572 | [Fact] | 581 | [Fact] |
| 582 | void PathGetParentPathTest() | ||
| 583 | { | ||
| 584 | HRESULT hr = S_OK; | ||
| 585 | LPWSTR sczPath = NULL; | ||
| 586 | LPCWSTR rgwzPaths[20] = | ||
| 587 | { | ||
| 588 | L"C:\\a\\b", L"C:\\a\\", | ||
| 589 | L"C:\\a\\b\\", L"C:\\a\\", | ||
| 590 | L"C:\\a", L"C:\\", | ||
| 591 | L"C:\\", NULL, | ||
| 592 | L"\"C:\\a\\b\\c\"", L"\"C:\\a\\b\\", | ||
| 593 | L"\"C:\\a\\b\\\"c", L"\"C:\\a\\b\\", | ||
| 594 | L"\"C:\\a\\b\"\\c", L"\"C:\\a\\b\"\\", | ||
| 595 | L"\"C:\\a\\\"b\\c", L"\"C:\\a\\\"b\\", | ||
| 596 | L"C:\\a\"\\\"b\\c", L"C:\\a\"\\\"b\\", | ||
| 597 | L"C:\\a\"\\b\\c\"", L"C:\\a\"\\b\\", | ||
| 598 | }; | ||
| 599 | |||
| 600 | try | ||
| 601 | { | ||
| 602 | for (DWORD i = 0; i < countof(rgwzPaths); i += 2) | ||
| 603 | { | ||
| 604 | hr = PathGetParentPath(rgwzPaths[i], &sczPath, NULL); | ||
| 605 | NativeAssert::Succeeded(hr, "PathGetParentPath: {0}", rgwzPaths[i]); | ||
| 606 | NativeAssert::StringEqual(rgwzPaths[i + 1], sczPath); | ||
| 607 | } | ||
| 608 | } | ||
| 609 | finally | ||
| 610 | { | ||
| 611 | ReleaseStr(sczPath); | ||
| 612 | } | ||
| 613 | } | ||
| 614 | |||
| 615 | [Fact] | ||
| 573 | void PathGetFullPathNameTest() | 616 | void PathGetFullPathNameTest() |
| 574 | { | 617 | { |
| 575 | HRESULT hr = S_OK; | 618 | HRESULT hr = S_OK; |
| @@ -693,6 +736,12 @@ namespace DutilTests | |||
| 693 | NativeAssert::StringEqual(L"Software\\Microsoft\\Windows\\", rgsczPaths[2]); | 736 | NativeAssert::StringEqual(L"Software\\Microsoft\\Windows\\", rgsczPaths[2]); |
| 694 | ReleaseNullStrArray(rgsczPaths, cPaths); | 737 | ReleaseNullStrArray(rgsczPaths, cPaths); |
| 695 | 738 | ||
| 739 | hr = PathGetHierarchyArray(L"Software", &rgsczPaths, &cPaths); | ||
| 740 | NativeAssert::Succeeded(hr, "Failed to get parent directories array for relative path"); | ||
| 741 | Assert::Equal<DWORD>(1, cPaths); | ||
| 742 | NativeAssert::StringEqual(L"Software", rgsczPaths[0]); | ||
| 743 | ReleaseNullStrArray(rgsczPaths, cPaths); | ||
| 744 | |||
| 696 | hr = PathGetHierarchyArray(L"c:/foo/bar/bas/a.txt", &rgsczPaths, &cPaths); | 745 | hr = PathGetHierarchyArray(L"c:/foo/bar/bas/a.txt", &rgsczPaths, &cPaths); |
| 697 | NativeAssert::Succeeded(hr, "Failed to get parent directories array for regular file path"); | 746 | NativeAssert::Succeeded(hr, "Failed to get parent directories array for regular file path"); |
| 698 | Assert::Equal<DWORD>(5, cPaths); | 747 | Assert::Equal<DWORD>(5, cPaths); |
| @@ -832,8 +881,12 @@ namespace DutilTests | |||
| 832 | hr = StrAllocString(&sczPath, rgwzPaths[i], 0); | 881 | hr = StrAllocString(&sczPath, rgwzPaths[i], 0); |
| 833 | NativeAssert::Succeeded(hr, "Failed to copy string"); | 882 | NativeAssert::Succeeded(hr, "Failed to copy string"); |
| 834 | 883 | ||
| 835 | hr = PathPrefix(&sczPath); | 884 | hr = PathPrefix(&sczPath, 0, 0); |
| 836 | NativeAssert::Succeeded(hr, "PathPrefix: {0}", rgwzPaths[i]); | 885 | NativeAssert::Succeeded(hr, "PathPrefix: {0}", rgwzPaths[i]); |
| 886 | NativeAssert::StringEqual(rgwzPaths[i], sczPath); | ||
| 887 | |||
| 888 | hr = PathPrefix(&sczPath, 0, PATH_PREFIX_SHORT_PATHS); | ||
| 889 | NativeAssert::Succeeded(hr, "PathPrefix (SHORT_PATHS): {0}", rgwzPaths[i]); | ||
| 837 | NativeAssert::StringEqual(rgwzPaths[i + 1], sczPath); | 890 | NativeAssert::StringEqual(rgwzPaths[i + 1], sczPath); |
| 838 | } | 891 | } |
| 839 | } | 892 | } |
| @@ -871,7 +924,7 @@ namespace DutilTests | |||
| 871 | hr = StrAllocString(&sczPath, rgwzPaths[i], 0); | 924 | hr = StrAllocString(&sczPath, rgwzPaths[i], 0); |
| 872 | NativeAssert::Succeeded(hr, "Failed to copy string"); | 925 | NativeAssert::Succeeded(hr, "Failed to copy string"); |
| 873 | 926 | ||
| 874 | hr = PathPrefix(&sczPath); | 927 | hr = PathPrefix(&sczPath, 0, PATH_PREFIX_EXPECT_FULLY_QUALIFIED); |
| 875 | NativeAssert::SpecificReturnCode(E_INVALIDARG, hr, "PathPrefix: {0}, {1}", rgwzPaths[i], sczPath); | 928 | NativeAssert::SpecificReturnCode(E_INVALIDARG, hr, "PathPrefix: {0}, {1}", rgwzPaths[i], sczPath); |
| 876 | } | 929 | } |
| 877 | } | 930 | } |
| @@ -884,187 +937,152 @@ namespace DutilTests | |||
| 884 | [Fact] | 937 | [Fact] |
| 885 | void PathIsRootedAndFullyQualifiedTest() | 938 | void PathIsRootedAndFullyQualifiedTest() |
| 886 | { | 939 | { |
| 887 | HRESULT hr = S_OK; | 940 | LPCWSTR rgwzPaths[30] = |
| 888 | LPWSTR sczPath = NULL; | 941 | { |
| 889 | LPCWSTR rgwzPaths[15] = | 942 | L"//", L"", |
| 890 | { | 943 | L"///", L"", |
| 891 | L"//", | 944 | L"C:/", L"", |
| 892 | L"///", | 945 | L"C://", L"/", |
| 893 | L"C:/", | 946 | L"C:/foo1", L"foo1", |
| 894 | L"C://", | 947 | L"C://foo2", L"/foo2", |
| 895 | L"C:/foo1", | 948 | L"//test/unc/path/to/something", L"path/to/something", |
| 896 | L"C://foo2", | 949 | L"//a/b/c/d/e", L"c/d/e", |
| 897 | L"//test/unc/path/to/something", | 950 | L"//a/b/", L"", |
| 898 | L"//a/b/c/d/e", | 951 | L"//a/b", L"", |
| 899 | L"//a/b/", | 952 | L"//test/unc", L"", |
| 900 | L"//a/b", | 953 | L"//Server", L"", |
| 901 | L"//test/unc", | 954 | L"//Server/Foo.txt", L"", |
| 902 | L"//Server", | 955 | L"//Server/Share/Foo.txt", L"Foo.txt", |
| 903 | L"//Server/Foo.txt", | 956 | L"//Server/Share/Test/Foo.txt", L"Test/Foo.txt", |
| 904 | L"//Server/Share/Foo.txt", | ||
| 905 | L"//Server/Share/Test/Foo.txt", | ||
| 906 | }; | 957 | }; |
| 907 | 958 | ||
| 908 | try | 959 | ValidateSkipPastRoot(rgwzPaths, countof(rgwzPaths), FALSE, TRUE, TRUE); |
| 909 | { | ||
| 910 | for (DWORD i = 0; i < countof(rgwzPaths); ++i) | ||
| 911 | { | ||
| 912 | ValidateFullyQualifiedPath(rgwzPaths[i], TRUE, FALSE); | ||
| 913 | ValidateRootedPath(rgwzPaths[i], TRUE); | ||
| 914 | |||
| 915 | hr = StrAllocString(&sczPath, rgwzPaths[i], 0); | ||
| 916 | NativeAssert::Succeeded(hr, "Failed to copy string"); | ||
| 917 | |||
| 918 | PathFixedReplaceForwardSlashes(sczPath); | ||
| 919 | ValidateFullyQualifiedPath(sczPath, TRUE, FALSE); | ||
| 920 | ValidateRootedPath(sczPath, TRUE); | ||
| 921 | } | ||
| 922 | } | ||
| 923 | finally | ||
| 924 | { | ||
| 925 | ReleaseStr(sczPath); | ||
| 926 | } | ||
| 927 | } | 960 | } |
| 928 | 961 | ||
| 929 | [Fact] | 962 | [Fact] |
| 930 | void PathIsRootedAndFullyQualifiedWithPrefixTest() | 963 | void PathIsRootedAndFullyQualifiedWithPrefixTest() |
| 931 | { | 964 | { |
| 932 | HRESULT hr = S_OK; | 965 | LPCWSTR rgwzPaths[12] = |
| 933 | LPWSTR sczPath = NULL; | ||
| 934 | LPCWSTR rgwzPaths[6] = | ||
| 935 | { | 966 | { |
| 936 | L"//?/UNC/test/unc/path/to/something", | 967 | L"//?/UNC/test/unc/path/to/something", L"path/to/something", |
| 937 | L"//?/UNC/test/unc", | 968 | L"//?/UNC/test/unc", L"", |
| 938 | L"//?/UNC/a/b1", | 969 | L"//?/UNC/a/b1", L"", |
| 939 | L"//?/UNC/a/b2/", | 970 | L"//?/UNC/a/b2/", L"", |
| 940 | L"//?/C:/foo/bar.txt", | 971 | L"//?/C:/foo/bar.txt", L"foo/bar.txt", |
| 941 | L"/??/C:/foo/bar.txt", | 972 | L"/??/C:/foo/bar.txt", L"foo/bar.txt", |
| 942 | }; | 973 | }; |
| 943 | 974 | ||
| 944 | try | 975 | ValidateSkipPastRoot(rgwzPaths, countof(rgwzPaths), TRUE, TRUE, TRUE); |
| 945 | { | ||
| 946 | for (DWORD i = 0; i < countof(rgwzPaths); ++i) | ||
| 947 | { | ||
| 948 | ValidateFullyQualifiedPath(rgwzPaths[i], TRUE, TRUE); | ||
| 949 | ValidateRootedPath(rgwzPaths[i], TRUE); | ||
| 950 | |||
| 951 | hr = StrAllocString(&sczPath, rgwzPaths[i], 0); | ||
| 952 | NativeAssert::Succeeded(hr, "Failed to copy string"); | ||
| 953 | |||
| 954 | PathFixedReplaceForwardSlashes(sczPath); | ||
| 955 | ValidateFullyQualifiedPath(sczPath, TRUE, TRUE); | ||
| 956 | ValidateRootedPath(sczPath, TRUE); | ||
| 957 | } | ||
| 958 | } | ||
| 959 | finally | ||
| 960 | { | ||
| 961 | ReleaseStr(sczPath); | ||
| 962 | } | ||
| 963 | } | 976 | } |
| 964 | 977 | ||
| 965 | [Fact] | 978 | [Fact] |
| 966 | void PathIsRootedButNotFullyQualifiedTest() | 979 | void PathIsRootedButNotFullyQualifiedTest() |
| 967 | { | 980 | { |
| 968 | HRESULT hr = S_OK; | 981 | LPCWSTR rgwzPaths[14] = |
| 969 | LPWSTR sczPath = NULL; | ||
| 970 | LPCWSTR rgwzPaths[7] = | ||
| 971 | { | 982 | { |
| 972 | L"/", | 983 | L"/", L"", |
| 973 | L"a:", | 984 | L"a:", L"", |
| 974 | L"A:", | 985 | L"A:", L"", |
| 975 | L"z:", | 986 | L"z:", L"", |
| 976 | L"Z:", | 987 | L"Z:", L"", |
| 977 | L"C:foo.txt", | 988 | L"C:foo.txt", L"foo.txt", |
| 978 | L"/dir", | 989 | L"/dir", L"dir", |
| 979 | }; | 990 | }; |
| 980 | 991 | ||
| 981 | try | 992 | ValidateSkipPastRoot(rgwzPaths, countof(rgwzPaths), FALSE, FALSE, TRUE); |
| 982 | { | ||
| 983 | for (DWORD i = 0; i < countof(rgwzPaths); ++i) | ||
| 984 | { | ||
| 985 | ValidateFullyQualifiedPath(rgwzPaths[i], FALSE, FALSE); | ||
| 986 | ValidateRootedPath(rgwzPaths[i], TRUE); | ||
| 987 | |||
| 988 | hr = StrAllocString(&sczPath, rgwzPaths[i], 0); | ||
| 989 | NativeAssert::Succeeded(hr, "Failed to copy string"); | ||
| 990 | |||
| 991 | PathFixedReplaceForwardSlashes(sczPath); | ||
| 992 | ValidateFullyQualifiedPath(sczPath, FALSE, FALSE); | ||
| 993 | ValidateRootedPath(sczPath, TRUE); | ||
| 994 | } | ||
| 995 | } | ||
| 996 | finally | ||
| 997 | { | ||
| 998 | ReleaseStr(sczPath); | ||
| 999 | } | ||
| 1000 | } | 993 | } |
| 1001 | 994 | ||
| 1002 | [Fact] | 995 | [Fact] |
| 1003 | void PathIsNotRootedAndNotFullyQualifiedTest() | 996 | void PathIsNotRootedAndNotFullyQualifiedTest() |
| 1004 | { | 997 | { |
| 1005 | HRESULT hr = S_OK; | 998 | LPCWSTR rgwzPaths[18] = |
| 1006 | LPWSTR sczPath = NULL; | ||
| 1007 | LPCWSTR rgwzPaths[9] = | ||
| 1008 | { | 999 | { |
| 1009 | NULL, | 1000 | NULL, NULL, |
| 1010 | L"", | 1001 | L"", NULL, |
| 1011 | L"dir", | 1002 | L"dir", NULL, |
| 1012 | L"dir/subdir", | 1003 | L"dir/subdir", NULL, |
| 1013 | L"@:/foo", // 064 = @ 065 = A | 1004 | L"@:/foo", NULL, // 064 = @ 065 = A |
| 1014 | L"[://", // 091 = [ 090 = Z | 1005 | L"[://", NULL, // 091 = [ 090 = Z |
| 1015 | L"`:/foo ", // 096 = ` 097 = a | 1006 | L"`:/foo ", NULL, // 096 = ` 097 = a |
| 1016 | L"{://", // 123 = { 122 = z | 1007 | L"{://", NULL, // 123 = { 122 = z |
| 1017 | L"[:", | 1008 | L"[:", NULL, |
| 1018 | }; | 1009 | }; |
| 1019 | 1010 | ||
| 1011 | ValidateSkipPastRoot(rgwzPaths, countof(rgwzPaths), FALSE, FALSE, FALSE); | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | void ValidateSkipPastRoot(LPCWSTR* rgwzPaths, DWORD cPaths, BOOL fExpectedPrefix, BOOL fExpectedFullyQualified, BOOL fExpectedRooted) | ||
| 1015 | { | ||
| 1016 | HRESULT hr = S_OK; | ||
| 1017 | LPWSTR sczPath = NULL; | ||
| 1018 | LPWSTR sczSkipRootPath = NULL; | ||
| 1019 | LPCWSTR wzSkipRootPath = NULL; | ||
| 1020 | BOOL fHasPrefix = FALSE; | ||
| 1021 | |||
| 1020 | try | 1022 | try |
| 1021 | { | 1023 | { |
| 1022 | for (DWORD i = 0; i < countof(rgwzPaths); ++i) | 1024 | for (DWORD i = 0; i < cPaths; i += 2) |
| 1023 | { | 1025 | { |
| 1024 | ValidateFullyQualifiedPath(rgwzPaths[i], FALSE, FALSE); | 1026 | wzSkipRootPath = PathSkipPastRoot(rgwzPaths[i], &fHasPrefix, NULL, NULL); |
| 1025 | ValidateRootedPath(rgwzPaths[i], FALSE); | 1027 | NativeAssert::StringEqual(rgwzPaths[i + 1], wzSkipRootPath); |
| 1028 | ValidateExtendedPrefixPath(rgwzPaths[i], fExpectedPrefix, fHasPrefix); | ||
| 1029 | ValidateFullyQualifiedPath(rgwzPaths[i], fExpectedFullyQualified); | ||
| 1030 | ValidateRootedPath(rgwzPaths[i], fExpectedRooted); | ||
| 1026 | 1031 | ||
| 1027 | if (!rgwzPaths[i]) | 1032 | if (rgwzPaths[i]) |
| 1028 | { | 1033 | { |
| 1029 | continue; | 1034 | hr = StrAllocString(&sczPath, rgwzPaths[i], 0); |
| 1035 | NativeAssert::Succeeded(hr, "Failed to copy string"); | ||
| 1036 | |||
| 1037 | PathFixedReplaceForwardSlashes(sczPath); | ||
| 1030 | } | 1038 | } |
| 1031 | 1039 | ||
| 1032 | hr = StrAllocString(&sczPath, rgwzPaths[i], 0); | 1040 | if (rgwzPaths[i + 1]) |
| 1033 | NativeAssert::Succeeded(hr, "Failed to copy string"); | 1041 | { |
| 1042 | hr = StrAllocString(&sczSkipRootPath, rgwzPaths[i + 1], 0); | ||
| 1043 | NativeAssert::Succeeded(hr, "Failed to copy string"); | ||
| 1034 | 1044 | ||
| 1035 | PathFixedReplaceForwardSlashes(sczPath); | 1045 | PathFixedReplaceForwardSlashes(sczSkipRootPath); |
| 1036 | ValidateFullyQualifiedPath(sczPath, FALSE, FALSE); | 1046 | } |
| 1037 | ValidateRootedPath(sczPath, FALSE); | 1047 | |
| 1048 | wzSkipRootPath = PathSkipPastRoot(sczPath, &fHasPrefix, NULL, NULL); | ||
| 1049 | NativeAssert::StringEqual(sczSkipRootPath, wzSkipRootPath); | ||
| 1050 | ValidateExtendedPrefixPath(sczPath, fExpectedPrefix, fHasPrefix); | ||
| 1051 | ValidateFullyQualifiedPath(sczPath, fExpectedFullyQualified); | ||
| 1052 | ValidateRootedPath(sczPath, fExpectedRooted); | ||
| 1038 | } | 1053 | } |
| 1039 | } | 1054 | } |
| 1040 | finally | 1055 | finally |
| 1041 | { | 1056 | { |
| 1042 | ReleaseStr(sczPath); | 1057 | ReleaseStr(sczPath); |
| 1058 | ReleaseStr(sczSkipRootPath); | ||
| 1043 | } | 1059 | } |
| 1044 | } | 1060 | } |
| 1045 | 1061 | ||
| 1046 | void ValidateFullyQualifiedPath(LPCWSTR wzPath, BOOL fExpected, BOOL fExpectedHasPrefix) | 1062 | void ValidateExtendedPrefixPath(LPCWSTR wzPath, BOOL fExpected, BOOL fHasExtendedPrefix) |
| 1047 | { | 1063 | { |
| 1048 | BOOL fHasLongPathPrefix = FALSE; | 1064 | String^ message = String::Format("HasExtendedPrefix: {0}", gcnew String(wzPath)); |
| 1049 | BOOL fRooted = PathIsFullyQualified(wzPath, &fHasLongPathPrefix); | ||
| 1050 | String^ message = String::Format("IsFullyQualified: {0}", gcnew String(wzPath)); | ||
| 1051 | if (fExpected) | 1065 | if (fExpected) |
| 1052 | { | 1066 | { |
| 1053 | Assert::True(fRooted, message); | 1067 | Assert::True(fHasExtendedPrefix, message); |
| 1054 | } | 1068 | } |
| 1055 | else | 1069 | else |
| 1056 | { | 1070 | { |
| 1057 | Assert::False(fRooted, message); | 1071 | Assert::False(fHasExtendedPrefix, message); |
| 1058 | } | 1072 | } |
| 1073 | } | ||
| 1059 | 1074 | ||
| 1060 | message = String::Format("HasLongPathPrefix: {0}", gcnew String(wzPath)); | 1075 | void ValidateFullyQualifiedPath(LPCWSTR wzPath, BOOL fExpected) |
| 1061 | if (fExpectedHasPrefix) | 1076 | { |
| 1077 | BOOL fRooted = PathIsFullyQualified(wzPath); | ||
| 1078 | String^ message = String::Format("IsFullyQualified: {0}", gcnew String(wzPath)); | ||
| 1079 | if (fExpected) | ||
| 1062 | { | 1080 | { |
| 1063 | Assert::True(fHasLongPathPrefix, message); | 1081 | Assert::True(fRooted, message); |
| 1064 | } | 1082 | } |
| 1065 | else | 1083 | else |
| 1066 | { | 1084 | { |
| 1067 | Assert::False(fHasLongPathPrefix, message); | 1085 | Assert::False(fRooted, message); |
| 1068 | } | 1086 | } |
| 1069 | } | 1087 | } |
| 1070 | 1088 | ||
