summaryrefslogtreecommitdiff
path: root/src/libs/dutil/WixToolset.DUtil/pathutil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil/pathutil.cpp')
-rw-r--r--src/libs/dutil/WixToolset.DUtil/pathutil.cpp296
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
121DAPI_(HRESULT) PathGetParentPath( 121DAPI_(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
149LExit: 170LExit:
@@ -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
321DAPI_(HRESULT) PathPrefix( 330DAPI_(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
361LExit: 395LExit:
@@ -970,55 +1004,114 @@ LExit:
970} 1004}
971 1005
972 1006
973DAPI_(BOOL) PathIsFullyQualified( 1007DAPI_(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])) 1087LExit:
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
1016LExit: 1098 if (pfUNC)
1017 if (pfHasLongPathPrefix)
1018 { 1099 {
1019 *pfHasLongPathPrefix = fHasLongPathPrefix; 1100 *pfUNC = fUNC;
1020 } 1101 }
1021 1102
1103 return wzPastRoot;
1104}
1105
1106
1107DAPI_(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
1190LExit: 1252LExit:
1191 ReleaseStr(sczPathCopy);
1192
1193 return hr; 1253 return hr;
1194} 1254}
1195 1255