diff options
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil/pathutil.cpp')
-rw-r--r-- | src/libs/dutil/WixToolset.DUtil/pathutil.cpp | 296 |
1 files changed, 178 insertions, 118 deletions
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 | ||