diff options
| author | Sean Hall <r.sean.hall@gmail.com> | 2022-05-26 17:33:41 -0500 |
|---|---|---|
| committer | Sean Hall <r.sean.hall@gmail.com> | 2022-05-31 13:20:44 -0500 |
| commit | dea25f58e6119ef1acc5c5cc2a7c98e52cdff519 (patch) | |
| tree | 853368043c98c9f8617744f8d8cb01b7a629e8a9 /src/libs/dutil/WixToolset.DUtil/pathutil.cpp | |
| parent | fb4f8c7108f43d2341ba299424646c4963b21188 (diff) | |
| download | wix-dea25f58e6119ef1acc5c5cc2a7c98e52cdff519.tar.gz wix-dea25f58e6119ef1acc5c5cc2a7c98e52cdff519.tar.bz2 wix-dea25f58e6119ef1acc5c5cc2a7c98e52cdff519.zip | |
Add PathCanonicalizeForComparison.
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil/pathutil.cpp')
| -rw-r--r-- | src/libs/dutil/WixToolset.DUtil/pathutil.cpp | 171 |
1 files changed, 145 insertions, 26 deletions
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 | ) |
