Add PathSkipPastRoot.
Sean Hall committed
Jun 3, 2022 at 17:48 UTC
6b0f2d978504da82070523eb6adb0b59f9812e93
9 files changed
+414
-310
src/burn/engine/bundlepackageengine.cpp
+1
-1
@@ -763,7 +763,7 @@ static HRESULT ExecuteBundle(
763
764
if (fPseudoPackage)
765
{
766
- if (!PathIsFullyQualified(pPackagePayload->sczFilePath, NULL))
766
+ if (!PathIsFullyQualified(pPackagePayload->sczFilePath))
767
{
768
ExitWithRootFailure(hr, E_INVALIDSTATE, "Related bundles must have a fully qualified target path.");
769
}
src/burn/engine/exeengine.cpp
+1
-1
@@ -363,7 +363,7 @@ extern "C" HRESULT ExeEngineExecutePackage(
363
364
if (pPackage->Exe.fPseudoPackage && BURN_PAYLOAD_VERIFICATION_UPDATE_BUNDLE != pPackagePayload->verification)
365
{
366
- if (!PathIsFullyQualified(pPackagePayload->sczFilePath, NULL))
366
+ if (!PathIsFullyQualified(pPackagePayload->sczFilePath))
367
{
368
ExitWithRootFailure(hr, E_INVALIDSTATE, "Pseudo ExePackages must have a fully qualified target path.");
369
}
src/burn/engine/logging.cpp
+4
-2
@@ -134,10 +134,12 @@ extern "C" HRESULT LoggingOpen(
134
135
if (sczPrefixFormatted && *sczPrefixFormatted)
136
{
137
+ // Best effort to open default logging.
138
LPCWSTR wzPrefix = sczPrefixFormatted;
139
+ LPCWSTR wzPastRoot = PathSkipPastRoot(sczPrefixFormatted, NULL, NULL, NULL);
140
139
- // Best effort to open default logging.
140
- if (PathIsRooted(sczPrefixFormatted))
141
+ // If the log path is rooted and has a file component, then use that path as is.
142
+ if (wzPastRoot && *wzPastRoot)
143
{
144
hr = PathGetDirectory(sczPrefixFormatted, &sczLoggingBaseFolder);
145
ExitOnFailure(hr, "Failed to get parent directory from '%ls'.", sczPrefixFormatted);
src/libs/dutil/WixToolset.DUtil/dirutil.cpp
+15
-5
@@ -364,17 +364,27 @@ extern "C" DWORD DAPI DirDeleteEmptyDirectoriesToRoot(
364
__in DWORD /*dwFlags*/
365
)
366
{
367
+ HRESULT hr = S_OK;
368
DWORD cDeletedDirs = 0;
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);
379
370
- while (wzPath && *wzPath && ::RemoveDirectoryW(wzPath))
380
+ cchRoot = wzPastRoot - sczPath;
381
+
382
+ while (sczPath && sczPath[cchRoot] && ::RemoveDirectoryW(sczPath))
383
{
384
++cDeletedDirs;
385
374
- HRESULT hr = PathGetParentPath(wzPath, &sczPath);
375
- DirExitOnFailure(hr, "Failed to get parent directory for path: %ls", wzPath);
376
-
377
- wzPath = sczPath;
386
+ hr = PathGetParentPath(sczPath, &sczPath, &cchRoot);
387
+ DirExitOnFailure(hr, "Failed to get parent directory for path: %ls", sczPath);
388
}
389
390
LExit:
src/libs/dutil/WixToolset.DUtil/inc/pathutil.h
+43
-12
@@ -8,8 +8,8 @@ extern "C" {
8
9
typedef enum _PATH_CANONICALIZE
10
{
11
- // Always prefix fully qualified paths with the long path prefix (\\?\).
12
- PATH_CANONICALIZE_APPEND_LONG_PATH_PREFIX = 0x0001,
11
+ // Always prefix fully qualified paths with the extended path prefix (\\?\).
12
+ PATH_CANONICALIZE_APPEND_EXTENDED_PATH_PREFIX = 0x0001,
13
// Always terminate the path with \.
14
PATH_CANONICALIZE_BACKSLASH_TERMINATE = 0x0002,
15
// Don't collapse . or .. in the \\server\share portion of a UNC path.
@@ -22,6 +22,14 @@ typedef enum _PATH_EXPAND
22
PATH_EXPAND_FULLPATH = 0x0002,
23
} PATH_EXPAND;
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
+
33
34
/*******************************************************************
35
PathFile - returns a pointer to the file part of the path.
@@ -40,8 +48,9 @@ DAPI_(LPCWSTR) PathExtension(
48
49
/*******************************************************************
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.
44
- Returns S_FALSE if the path only contains a file name.
51
+ Calling the function again with the previous result returns the same result.
52
+ Returns S_FALSE if the path only contains a file name.
53
+ For example, C:\a\b -> C:\a\ -> C:\a\
54
********************************************************************/
55
DAPI_(HRESULT) PathGetDirectory(
56
__in_z LPCWSTR wzPath,
@@ -49,12 +58,18 @@ DAPI_(HRESULT) PathGetDirectory(
58
);
59
60
/*******************************************************************
52
-PathGetParentPath - extracts the parent directory from a full path.
53
- *psczDirectory is NULL if the path only contains a file name.
61
+PathGetParentPath - extracts the parent directory from a path
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
68
********************************************************************/
69
DAPI_(HRESULT) PathGetParentPath(
70
__in_z LPCWSTR wzPath,
57
- __out_z LPWSTR *psczDirectory
71
+ __out_z LPWSTR *psczDirectory,
72
+ __out_opt SIZE_T* pcchRoot
73
);
74
75
/*******************************************************************
@@ -78,11 +93,14 @@ DAPI_(HRESULT) PathGetFullPathName(
93
);
94
95
/*******************************************************************
81
- PathPrefix - prefixes a full path with \\?\ or \\?\UNC as
82
- appropriate.
96
+ PathPrefix - prefixes a path with \\?\ or \\?\UNC if it doesn't
97
+ already have an extended prefix, is longer than MAX_PATH,
98
+ and is fully qualified.
99
********************************************************************/
100
DAPI_(HRESULT) PathPrefix(
85
- __inout LPWSTR *psczFullPath
101
+ __inout_z LPWSTR *psczFullPath,
102
+ __in SIZE_T cchFullPath,
103
+ __in DWORD dwPrefixFlags
104
);
105
106
/*******************************************************************
@@ -203,6 +221,20 @@ DAPI_(HRESULT) PathGetKnownFolder(
221
__out LPWSTR* psczKnownFolder
222
);
223
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
/*******************************************************************
239
PathIsFullyQualified - returns true if the path is fully qualified; false otherwise.
240
Note that some rooted paths like C:dir are not fully qualified.
@@ -210,8 +242,7 @@ DAPI_(HRESULT) PathGetKnownFolder(
242
For example, these are not fully qualified: C:dir, C:, \dir, dir, dir\subdir.
243
*******************************************************************/
244
DAPI_(BOOL) PathIsFullyQualified(
213
- __in_z LPCWSTR wzPath,
214
- __out_opt BOOL* pfHasLongPathPrefix
245
+ __in_z LPCWSTR wzPath
246
);
247
248
/*******************************************************************
src/libs/dutil/WixToolset.DUtil/logutil.cpp
+1
-1
@@ -133,7 +133,7 @@ extern "C" HRESULT DAPI LogOpen(
133
hr = PathConcat(wzDirectory, wzLog, &sczCombined);
134
LoguExitOnFailure(hr, "Failed to combine the log path.");
135
136
- if (!PathIsFullyQualified(sczCombined, NULL))
136
+ if (!PathIsFullyQualified(sczCombined))
137
{
138
hr = PathExpand(&LogUtil_sczLogPath, sczCombined, PATH_EXPAND_FULLPATH);
139
LoguExitOnFailure(hr, "Failed to expand the log path.");
src/libs/dutil/WixToolset.DUtil/path2utl.cpp
+18
-35
@@ -64,31 +64,12 @@ DAPI_(HRESULT) PathCanonicalizeForComparison(
64
65
if (PATH_CANONICALIZE_KEEP_UNC_ROOT & dwCanonicalizeFlags)
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)
70
{
69
- if (L'U' == sczNormalizedPath[4] && L'N' == sczNormalizedPath[5] && L'C' == sczNormalizedPath[6] && L'\\' == sczNormalizedPath[7])
70
- {
71
- cchUncRootLength = 8;
72
- }
73
- }
74
- else if (L'\\' == sczNormalizedPath[0] && L'\\' == sczNormalizedPath[1])
75
- {
76
- cchUncRootLength = 2;
77
- }
78
-
79
- if (cchUncRootLength)
80
- {
81
- DWORD dwRemainingSlashes = 2;
82
-
83
- for (wzNormalizedPath += cchUncRootLength; *wzNormalizedPath && dwRemainingSlashes; ++wzNormalizedPath)
84
- {
85
- ++cchUncRootLength;
86
-
87
- if (L'\\' == *wzNormalizedPath)
88
- {
89
- --dwRemainingSlashes;
90
- }
91
- }
71
+ wzNormalizedPath = wzPastRoot;
72
+ cchUncRootLength = wzPastRoot - sczNormalizedPath;
73
}
74
}
75
@@ -115,22 +96,24 @@ DAPI_(HRESULT) PathCanonicalizeForComparison(
96
if (PATH_CANONICALIZE_BACKSLASH_TERMINATE & dwCanonicalizeFlags)
97
{
98
hr = PathBackslashTerminate(psczCanonicalized);
118
- PathExitOnFailure(hr, "Failed to backslash terminate the canonicalized path");
99
+ PathExitOnFailure(hr, "Failed to backslash terminate the canonicalized path.");
100
}
101
121
- if (PathIsFullyQualified(*psczCanonicalized, &fHasPrefix) && !fHasPrefix &&
122
- (PATH_CANONICALIZE_APPEND_LONG_PATH_PREFIX & dwCanonicalizeFlags))
102
+ if (PATH_CANONICALIZE_APPEND_EXTENDED_PATH_PREFIX & dwCanonicalizeFlags)
103
{
124
- hr = PathPrefix(psczCanonicalized);
125
- PathExitOnFailure(hr, "Failed to ensure the long path prefix on the canonicalized path");
126
-
127
- fHasPrefix = TRUE;
104
+ hr = PathPrefix(psczCanonicalized, 0, PATH_PREFIX_SHORT_PATHS);
105
+ PathExitOnFailure(hr, "Failed to ensure the extended path prefix on the canonicalized path.");
106
}
107
108
+ PathSkipPastRoot(*psczCanonicalized, &fHasPrefix, NULL, NULL);
109
+
110
if (fHasPrefix)
111
{
132
- // Canonicalize \??\ into \\?\.
112
+ // Canonicalize prefix into \\?\.
113
+ (*psczCanonicalized)[0] = L'\\';
114
(*psczCanonicalized)[1] = L'\\';
115
+ (*psczCanonicalized)[2] = L'?';
116
+ (*psczCanonicalized)[3] = L'\\';
117
}
118
119
LExit:
@@ -188,7 +171,7 @@ DAPI_(HRESULT) PathCompareCanonicalized(
171
HRESULT hr = S_OK;
172
LPWSTR sczCanonicalized1 = 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;
175
int nResult = 0;
176
177
if (!wzPath1 || !wzPath2)
@@ -221,7 +204,7 @@ DAPI_(HRESULT) PathDirectoryContainsPath(
204
HRESULT hr = S_OK;
205
LPWSTR sczCanonicalizedDirectory = 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;
208
size_t cchDirectory = 0;
209
210
if (!wzDirectory || !*wzDirectory)
@@ -239,7 +222,7 @@ DAPI_(HRESULT) PathDirectoryContainsPath(
222
hr = PathCanonicalizeForComparison(wzPath, dwDefaultFlags, &sczCanonicalizedPath);
223
PathExitOnFailure(hr, "Failed to canonicalize the path.");
224
242
- if (!PathIsFullyQualified(sczCanonicalizedDirectory, NULL))
225
+ if (!PathIsFullyQualified(sczCanonicalizedDirectory))
226
{
227
PathExitWithRootFailure(hr, E_INVALIDARG, "wzDirectory must be a fully qualified path.");
228
}
src/libs/dutil/WixToolset.DUtil/pathutil.cpp
+178
-118
@@ -120,13 +120,34 @@ LExit:
120
121
DAPI_(HRESULT) PathGetParentPath(
122
__in_z LPCWSTR wzPath,
123
- __out_z LPWSTR *psczParent
123
+ __out_z LPWSTR* psczParent,
124
+ __out_opt SIZE_T* pcchRoot
125
)
126
{
127
HRESULT hr = S_OK;
128
+ LPCWSTR wzPastRoot = NULL;
129
LPCWSTR wzParent = NULL;
130
+ LPCWSTR wz = NULL;
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)
151
{
152
if (IsPathSeparatorChar(*wz) && wz[1])
153
{
@@ -143,7 +164,7 @@ DAPI_(HRESULT) PathGetParentPath(
164
}
165
else
166
{
146
- ReleaseNullStr(psczParent);
167
+ ReleaseNullStr(*psczParent);
168
}
169
170
LExit:
@@ -164,9 +185,8 @@ DAPI_(HRESULT) PathExpand(
185
LPWSTR sczExpandedPath = NULL;
186
SIZE_T cchWritten = 0;
187
DWORD cchExpandedPath = 0;
167
- SIZE_T cbSize = 0;
168
-
188
LPWSTR sczFullPath = NULL;
189
+ DWORD dwPrefixFlags = 0;
190
191
//
192
// First, expand any environment variables.
@@ -201,20 +221,7 @@ DAPI_(HRESULT) PathExpand(
221
}
222
}
223
204
- if (MAX_PATH < 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
- }
224
+ cchWritten = cch;
225
}
226
227
//
@@ -227,11 +234,7 @@ DAPI_(HRESULT) PathExpand(
234
hr = PathGetFullPathName(wzPath, &sczFullPath, NULL, &cchWritten);
235
PathExitOnFailure(hr, "Failed to get full path for string: %ls", wzPath);
236
230
- if (MAX_PATH < cchWritten)
231
- {
232
- hr = PathPrefix(&sczFullPath);
233
- PathExitOnFailure(hr, "Failed to prefix long path after expanding.");
234
- }
237
+ dwPrefixFlags |= PATH_PREFIX_EXPECT_FULLY_QUALIFIED;
238
}
239
else
240
{
@@ -239,6 +242,12 @@ DAPI_(HRESULT) PathExpand(
242
sczExpandedPath = NULL;
243
}
244
245
+ if (dwResolveFlags)
246
+ {
247
+ hr = PathPrefix(&sczFullPath, cchWritten, dwPrefixFlags);
248
+ PathExitOnFailure(hr, "Failed to prefix path after expanding.");
249
+ }
250
+
251
hr = StrAllocString(psczFullPath, sczFullPath ? sczFullPath : wzRelativePath, 0);
252
PathExitOnFailure(hr, "Failed to copy relative path into full path.");
253
@@ -319,29 +328,54 @@ LExit:
328
329
330
DAPI_(HRESULT) PathPrefix(
322
- __inout LPWSTR *psczFullPath
331
+ __inout_z LPWSTR* psczFullPath,
332
+ __in SIZE_T cchFullPath,
333
+ __in DWORD dwPrefixFlags
334
)
335
{
325
- Assert(psczFullPath && *psczFullPath);
336
+ Assert(psczFullPath);
337
338
HRESULT hr = S_OK;
339
LPWSTR wzFullPath = *psczFullPath;
340
BOOL fFullyQualified = FALSE;
341
BOOL fHasPrefix = FALSE;
342
+ BOOL fUNC = FALSE;
343
SIZE_T cbFullPath = 0;
344
333
- fFullyQualified = PathIsFullyQualified(wzFullPath, &fHasPrefix);
345
+ PathSkipPastRoot(wzFullPath, &fHasPrefix, &fFullyQualified, &fUNC);
346
+
347
if (fHasPrefix)
348
{
349
ExitFunction();
350
}
351
339
- if (fFullyQualified && L':' == wzFullPath[1]) // normal path
352
+ // The prefix is only allowed on fully qualified paths.
353
+ if (!fFullyQualified)
354
{
341
- hr = StrAllocPrefix(psczFullPath, L"\\\\?\\", 4);
342
- PathExitOnFailure(hr, "Failed to add prefix to file path.");
355
+ if (dwPrefixFlags & PATH_PREFIX_EXPECT_FULLY_QUALIFIED)
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
+ }
376
}
344
- else if (fFullyQualified && IsPathSeparatorChar(wzFullPath[1])) // UNC
377
+
378
+ if (fUNC)
379
{
380
hr = StrSize(*psczFullPath, &cbFullPath);
381
PathExitOnFailure(hr, "Failed to get size of full path.");
@@ -352,10 +386,10 @@ DAPI_(HRESULT) PathPrefix(
386
hr = StrAllocPrefix(psczFullPath, L"\\\\?\\UNC", 7);
387
PathExitOnFailure(hr, "Failed to add prefix to UNC path.");
388
}
355
- else
389
+ else // must be a normal path
390
{
357
- hr = E_INVALIDARG;
358
- PathExitOnFailure(hr, "Invalid path provided to prefix: %ls.", wzFullPath);
391
+ hr = StrAllocPrefix(psczFullPath, L"\\\\?\\", 4);
392
+ PathExitOnFailure(hr, "Failed to add prefix to file path.");
393
}
394
395
LExit:
@@ -970,55 +1004,114 @@ LExit:
1004
}
1005
1006
973
-DAPI_(BOOL) PathIsFullyQualified(
974
- __in_z LPCWSTR wzPath,
975
- __out_opt BOOL* pfHasLongPathPrefix
1007
+DAPI_(LPCWSTR) PathSkipPastRoot(
1008
+ __in_z_opt LPCWSTR wzPath,
1009
+ __out_opt BOOL* pfHasExtendedPrefix,
1010
+ __out_opt BOOL* pfFullyQualified,
1011
+ __out_opt BOOL* pfUNC
1012
)
1013
{
1014
+ LPCWSTR wzPastRoot = NULL;
1015
+ BOOL fHasPrefix = FALSE;
1016
BOOL fFullyQualified = FALSE;
979
- BOOL fHasLongPathPrefix = FALSE;
1017
+ BOOL fUNC = FALSE;
1018
+ DWORD dwRootMissingSlashes = 0;
1019
981
- if (!wzPath || !wzPath[0] || !wzPath[1])
1020
+ if (!wzPath || !*wzPath)
1021
{
983
- // There is no way to specify a fully qualified path with one character (or less).
1022
ExitFunction();
1023
}
1024
987
- if (!IsPathSeparatorChar(wzPath[0]))
1025
+ if (IsPathSeparatorChar(wzPath[0]))
1026
{
989
- // The only way to specify a fully qualified path that doesn't begin with a slash
990
- // is the drive, colon, slash format (C:\).
991
- if (IsValidDriveChar(wzPath[0]) &&
992
- L':' == wzPath[1] &&
993
- IsPathSeparatorChar(wzPath[2]))
1027
+ if (IsPathSeparatorChar(wzPath[1]) && (L'?' == wzPath[2] || L'.' == wzPath[2]) && IsPathSeparatorChar(wzPath[3]) ||
1028
+ L'?' == wzPath[1] && L'?' == wzPath[2] && IsPathSeparatorChar(wzPath[3]))
1029
{
995
- fFullyQualified = TRUE;
996
- }
1030
+ fHasPrefix = TRUE;
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
+ }
1050
}
1051
1001
- // Non-drive fully qualified paths must start with \\ or \?.
1002
- // \??\ is an archaic form of \\?\.
1003
- if (L'?' != wzPath[1] && !IsPathSeparatorChar(wzPath[1]))
1052
+ if (dwRootMissingSlashes)
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
+ }
1064
}
1065
+ else
1066
+ {
1067
+ Assert(!wzPastRoot);
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
+ }
1086
1010
- if (L'?' == wzPath[2] && IsPathSeparatorChar(wzPath[3]))
1087
+LExit:
1088
+ if (pfHasExtendedPrefix)
1089
{
1012
- fHasLongPathPrefix = TRUE;
1090
+ *pfHasExtendedPrefix = fHasPrefix;
1091
}
1092
1093
+ if (pfFullyQualified)
1094
+ {
1095
+ *pfFullyQualified = fFullyQualified;
1096
+ }
1097
1016
-LExit:
1017
- if (pfHasLongPathPrefix)
1098
+ if (pfUNC)
1099
{
1019
- *pfHasLongPathPrefix = fHasLongPathPrefix;
1100
+ *pfUNC = fUNC;
1101
}
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
+
1115
return fFullyQualified;
1116
}
1117
@@ -1027,9 +1120,7 @@ DAPI_(BOOL) PathIsRooted(
1120
__in_z LPCWSTR wzPath
1121
)
1122
{
1030
- return wzPath &&
1031
- (IsPathSeparatorChar(wzPath[0]) ||
1032
- IsValidDriveChar(wzPath[0]) && wzPath[1] == L':');
1123
+ return NULL != PathSkipPastRoot(wzPath, NULL, NULL, NULL);
1124
}
1125
1126
@@ -1118,78 +1209,47 @@ DAPI_(HRESULT) PathGetHierarchyArray(
1209
)
1210
{
1211
HRESULT hr = S_OK;
1121
- LPWSTR sczPathCopy = NULL;
1122
- LPWSTR sczNewPathCopy = NULL;
1123
- DWORD cArraySpacesNeeded = 0;
1124
- size_t cchPath = 0;
1212
+ LPCWSTR wz = NULL;
1213
+ SIZE_T cch = 0;
1214
+ *pcPathArray = 0;
1215
1126
- hr = ::StringCchLengthW(wzPath, STRSAFE_MAX_LENGTH, &cchPath);
1127
- PathExitOnRootFailure(hr, "Failed to get string length of path: %ls", wzPath);
1216
+ PathExitOnNull(wzPath, hr, E_INVALIDARG, "wzPath is required.");
1217
1129
- if (!cchPath)
1218
+ wz = PathSkipPastRoot(wzPath, NULL, NULL, NULL);
1219
+ if (wz)
1220
{
1131
- ExitFunction1(hr = E_INVALIDARG);
1132
- }
1221
+ cch = wz - wzPath;
1222
1134
- for (size_t i = 0; i < cchPath; ++i)
1135
- {
1136
- if (IsPathSeparatorChar(wzPath[i]))
1137
- {
1138
- ++cArraySpacesNeeded;
1139
- }
1140
- }
1223
+ hr = MemEnsureArraySize(reinterpret_cast<void**>(prgsczPathArray), 1, sizeof(LPWSTR), 5);
1224
+ PathExitOnFailure(hr, "Failed to allocate array.");
1225
1142
- if (!IsPathSeparatorChar(wzPath[cchPath - 1]))
1143
- {
1144
- ++cArraySpacesNeeded;
1145
- }
1226
+ hr = StrAllocString(*prgsczPathArray, wzPath, cch);
1227
+ PathExitOnFailure(hr, "Failed to copy root into array.");
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.
1148
- if (IsPathSeparatorChar(wzPath[0]) && IsPathSeparatorChar(wzPath[1]))
1229
+ *pcPathArray += 1;
1230
+ }
1231
+ else
1232
{
1150
- if (3 > cArraySpacesNeeded)
1151
- {
1152
- ExitFunction1(hr = E_INVALIDARG);
1153
- }
1154
-
1155
- cArraySpacesNeeded -= 3;
1233
+ wz = wzPath;
1234
}
1235
1158
- Assert(cArraySpacesNeeded >= 1);
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)
1236
+ for (; *wz; ++wz)
1237
{
1169
- hr = StrAllocString((*prgsczPathArray) + cArraySpacesNeeded - 1 - i, sczPathCopy, 0);
1170
- PathExitOnFailure(hr, "Failed to copy path");
1238
+ ++cch;
1239
1172
- DWORD cchPathCopy = lstrlenW(sczPathCopy);
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]))
1240
+ if (IsPathSeparatorChar(*wz) || !wz[1])
1241
{
1177
- sczPathCopy[cchPathCopy - 1] = L'\0';
1178
- }
1179
-
1180
- hr = PathGetDirectory(sczPathCopy, &sczNewPathCopy);
1181
- PathExitOnFailure(hr, "Failed to get directory portion of path");
1242
+ hr = MemEnsureArraySizeForNewItems(reinterpret_cast<void**>(prgsczPathArray), *pcPathArray, 1, sizeof(LPWSTR), 5);
1243
+ PathExitOnFailure(hr, "Failed to allocate array.");
1244
1183
- ReleaseStr(sczPathCopy);
1184
- sczPathCopy = sczNewPathCopy;
1185
- sczNewPathCopy = NULL;
1186
- }
1245
+ hr = StrAllocString(*prgsczPathArray + *pcPathArray, wzPath, cch);
1246
+ PathExitOnFailure(hr, "Failed to copy path into array.");
1247
1188
- hr = S_OK;
1248
+ *pcPathArray += 1;
1249
+ }
1250
+ }
1251
1252
LExit:
1191
- ReleaseStr(sczPathCopy);
1192
-
1253
return hr;
1254
}
1255
src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp
+153
-135
@@ -135,14 +135,22 @@ namespace DutilTests
135
NativeAssert::Succeeded(hr, "Failed to canonicalize path");
136
NativeAssert::StringEqual(L"\\\\server\\share\\", sczCanonicalized);
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");
140
- NativeAssert::StringEqual(L"\\\\.\\share\\otherdir\\unc.exe", sczCanonicalized);
140
+ NativeAssert::StringEqual(L"\\\\?\\UNC\\server\\share\\unc.exe", sczCanonicalized);
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);
147
NativeAssert::Succeeded(hr, "Failed to canonicalize path");
148
NativeAssert::StringEqual(L"\\\\share\\otherdir\\unc.exe", sczCanonicalized);
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
+
154
hr = PathCanonicalizeForComparison(L"\\\\server\\share\\..\\..\\otherdir\\unc.exe", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized);
155
NativeAssert::Succeeded(hr, "Failed to canonicalize path");
156
NativeAssert::StringEqual(L"\\\\server\\share\\otherdir\\unc.exe", sczCanonicalized);
@@ -215,7 +223,7 @@ namespace DutilTests
223
NativeAssert::Succeeded(hr, "Failed to canonicalize path");
224
NativeAssert::StringEqual(L"C:\\invalid:pathchars?.exe", sczCanonicalized);
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);
227
NativeAssert::Succeeded(hr, "Failed to canonicalize path");
228
NativeAssert::StringEqual(L"\\\\?\\C:\\addprefix.exe", sczCanonicalized);
229
@@ -312,7 +320,7 @@ namespace DutilTests
320
LPCWSTR rgwzPaths[8] =
321
{
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",
324
L"\\\\server\\.\\otherdir\\unc.exe", L"\\\\server\\otherdir\\unc.exe",
325
L"\\\\server\\\\otherdir\\unc.exe", L"\\\\server\\otherdir\\unc.exe",
326
};
@@ -541,9 +549,10 @@ namespace DutilTests
549
{
550
HRESULT hr = S_OK;
551
LPWSTR sczPath = NULL;
544
- LPCWSTR rgwzPaths[18] =
552
+ LPCWSTR rgwzPaths[20] =
553
{
554
L"C:\\a\\b", L"C:\\a\\",
555
+ L"C:\\a\\b\\", L"C:\\a\\b\\",
556
L"C:\\a", L"C:\\",
557
L"C:\\", L"C:\\",
558
L"\"C:\\a\\b\\c\"", L"\"C:\\a\\b\\",
@@ -569,6 +578,40 @@ namespace DutilTests
578
}
579
}
580
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]
616
void PathGetFullPathNameTest()
617
{
@@ -693,6 +736,12 @@ namespace DutilTests
736
NativeAssert::StringEqual(L"Software\\Microsoft\\Windows\\", rgsczPaths[2]);
737
ReleaseNullStrArray(rgsczPaths, cPaths);
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
+
745
hr = PathGetHierarchyArray(L"c:/foo/bar/bas/a.txt", &rgsczPaths, &cPaths);
746
NativeAssert::Succeeded(hr, "Failed to get parent directories array for regular file path");
747
Assert::Equal<DWORD>(5, cPaths);
@@ -832,8 +881,12 @@ namespace DutilTests
881
hr = StrAllocString(&sczPath, rgwzPaths[i], 0);
882
NativeAssert::Succeeded(hr, "Failed to copy string");
883
835
- hr = PathPrefix(&sczPath);
884
+ hr = PathPrefix(&sczPath, 0, 0);
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]);
890
NativeAssert::StringEqual(rgwzPaths[i + 1], sczPath);
891
}
892
}
@@ -871,7 +924,7 @@ namespace DutilTests
924
hr = StrAllocString(&sczPath, rgwzPaths[i], 0);
925
NativeAssert::Succeeded(hr, "Failed to copy string");
926
874
- hr = PathPrefix(&sczPath);
927
+ hr = PathPrefix(&sczPath, 0, PATH_PREFIX_EXPECT_FULLY_QUALIFIED);
928
NativeAssert::SpecificReturnCode(E_INVALIDARG, hr, "PathPrefix: {0}, {1}", rgwzPaths[i], sczPath);
929
}
930
}
@@ -884,187 +937,152 @@ namespace DutilTests
937
[Fact]
938
void PathIsRootedAndFullyQualifiedTest()
939
{
887
- HRESULT hr = S_OK;
888
- LPWSTR sczPath = NULL;
889
- LPCWSTR rgwzPaths[15] =
890
- {
891
- L"//",
892
- L"///",
893
- L"C:/",
894
- L"C://",
895
- L"C:/foo1",
896
- L"C://foo2",
897
- L"//test/unc/path/to/something",
898
- L"//a/b/c/d/e",
899
- L"//a/b/",
900
- L"//a/b",
901
- L"//test/unc",
902
- L"//Server",
903
- L"//Server/Foo.txt",
904
- L"//Server/Share/Foo.txt",
905
- L"//Server/Share/Test/Foo.txt",
940
+ LPCWSTR rgwzPaths[30] =
941
+ {
942
+ L"//", L"",
943
+ L"///", L"",
944
+ L"C:/", L"",
945
+ L"C://", L"/",
946
+ L"C:/foo1", L"foo1",
947
+ L"C://foo2", L"/foo2",
948
+ L"//test/unc/path/to/something", L"path/to/something",
949
+ L"//a/b/c/d/e", L"c/d/e",
950
+ L"//a/b/", L"",
951
+ L"//a/b", L"",
952
+ L"//test/unc", L"",
953
+ L"//Server", L"",
954
+ L"//Server/Foo.txt", L"",
955
+ L"//Server/Share/Foo.txt", L"Foo.txt",
956
+ L"//Server/Share/Test/Foo.txt", L"Test/Foo.txt",
957
};
958
908
- try
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
- }
959
+ ValidateSkipPastRoot(rgwzPaths, countof(rgwzPaths), FALSE, TRUE, TRUE);
960
}
961
962
[Fact]
963
void PathIsRootedAndFullyQualifiedWithPrefixTest()
964
{
932
- HRESULT hr = S_OK;
933
- LPWSTR sczPath = NULL;
934
- LPCWSTR rgwzPaths[6] =
965
+ LPCWSTR rgwzPaths[12] =
966
{
936
- L"//?/UNC/test/unc/path/to/something",
937
- L"//?/UNC/test/unc",
938
- L"//?/UNC/a/b1",
939
- L"//?/UNC/a/b2/",
940
- L"//?/C:/foo/bar.txt",
941
- L"/??/C:/foo/bar.txt",
967
+ L"//?/UNC/test/unc/path/to/something", L"path/to/something",
968
+ L"//?/UNC/test/unc", L"",
969
+ L"//?/UNC/a/b1", L"",
970
+ L"//?/UNC/a/b2/", L"",
971
+ L"//?/C:/foo/bar.txt", L"foo/bar.txt",
972
+ L"/??/C:/foo/bar.txt", L"foo/bar.txt",
973
};
974
944
- try
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
- }
975
+ ValidateSkipPastRoot(rgwzPaths, countof(rgwzPaths), TRUE, TRUE, TRUE);
976
}
977
978
[Fact]
979
void PathIsRootedButNotFullyQualifiedTest()
980
{
968
- HRESULT hr = S_OK;
969
- LPWSTR sczPath = NULL;
970
- LPCWSTR rgwzPaths[7] =
981
+ LPCWSTR rgwzPaths[14] =
982
{
972
- L"/",
973
- L"a:",
974
- L"A:",
975
- L"z:",
976
- L"Z:",
977
- L"C:foo.txt",
978
- L"/dir",
983
+ L"/", L"",
984
+ L"a:", L"",
985
+ L"A:", L"",
986
+ L"z:", L"",
987
+ L"Z:", L"",
988
+ L"C:foo.txt", L"foo.txt",
989
+ L"/dir", L"dir",
990
};
991
981
- try
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
- }
992
+ ValidateSkipPastRoot(rgwzPaths, countof(rgwzPaths), FALSE, FALSE, TRUE);
993
}
994
995
[Fact]
996
void PathIsNotRootedAndNotFullyQualifiedTest()
997
{
1005
- HRESULT hr = S_OK;
1006
- LPWSTR sczPath = NULL;
1007
- LPCWSTR rgwzPaths[9] =
998
+ LPCWSTR rgwzPaths[18] =
999
{
1009
- NULL,
1010
- L"",
1011
- L"dir",
1012
- L"dir/subdir",
1013
- L"@:/foo", // 064 = @ 065 = A
1014
- L"[://", // 091 = [ 090 = Z
1015
- L"`:/foo ", // 096 = ` 097 = a
1016
- L"{://", // 123 = { 122 = z
1017
- L"[:",
1000
+ NULL, NULL,
1001
+ L"", NULL,
1002
+ L"dir", NULL,
1003
+ L"dir/subdir", NULL,
1004
+ L"@:/foo", NULL, // 064 = @ 065 = A
1005
+ L"[://", NULL, // 091 = [ 090 = Z
1006
+ L"`:/foo ", NULL, // 096 = ` 097 = a
1007
+ L"{://", NULL, // 123 = { 122 = z
1008
+ L"[:", NULL,
1009
};
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
+
1022
try
1023
{
1022
- for (DWORD i = 0; i < countof(rgwzPaths); ++i)
1024
+ for (DWORD i = 0; i < cPaths; i += 2)
1025
{
1024
- ValidateFullyQualifiedPath(rgwzPaths[i], FALSE, FALSE);
1025
- ValidateRootedPath(rgwzPaths[i], FALSE);
1026
+ wzSkipRootPath = PathSkipPastRoot(rgwzPaths[i], &fHasPrefix, NULL, NULL);
1027
+ NativeAssert::StringEqual(rgwzPaths[i + 1], wzSkipRootPath);
1028
+ ValidateExtendedPrefixPath(rgwzPaths[i], fExpectedPrefix, fHasPrefix);
1029
+ ValidateFullyQualifiedPath(rgwzPaths[i], fExpectedFullyQualified);
1030
+ ValidateRootedPath(rgwzPaths[i], fExpectedRooted);
1031
1027
- if (!rgwzPaths[i])
1032
+ if (rgwzPaths[i])
1033
{
1029
- continue;
1034
+ hr = StrAllocString(&sczPath, rgwzPaths[i], 0);
1035
+ NativeAssert::Succeeded(hr, "Failed to copy string");
1036
+
1037
+ PathFixedReplaceForwardSlashes(sczPath);
1038
}
1039
1032
- hr = StrAllocString(&sczPath, rgwzPaths[i], 0);
1033
- NativeAssert::Succeeded(hr, "Failed to copy string");
1040
+ if (rgwzPaths[i + 1])
1041
+ {
1042
+ hr = StrAllocString(&sczSkipRootPath, rgwzPaths[i + 1], 0);
1043
+ NativeAssert::Succeeded(hr, "Failed to copy string");
1044
1035
- PathFixedReplaceForwardSlashes(sczPath);
1036
- ValidateFullyQualifiedPath(sczPath, FALSE, FALSE);
1037
- ValidateRootedPath(sczPath, FALSE);
1045
+ PathFixedReplaceForwardSlashes(sczSkipRootPath);
1046
+ }
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);
1053
}
1054
}
1055
finally
1056
{
1057
ReleaseStr(sczPath);
1058
+ ReleaseStr(sczSkipRootPath);
1059
}
1060
}
1061
1046
- void ValidateFullyQualifiedPath(LPCWSTR wzPath, BOOL fExpected, BOOL fExpectedHasPrefix)
1062
+ void ValidateExtendedPrefixPath(LPCWSTR wzPath, BOOL fExpected, BOOL fHasExtendedPrefix)
1063
{
1048
- BOOL fHasLongPathPrefix = FALSE;
1049
- BOOL fRooted = PathIsFullyQualified(wzPath, &fHasLongPathPrefix);
1050
- String^ message = String::Format("IsFullyQualified: {0}", gcnew String(wzPath));
1064
+ String^ message = String::Format("HasExtendedPrefix: {0}", gcnew String(wzPath));
1065
if (fExpected)
1066
{
1053
- Assert::True(fRooted, message);
1067
+ Assert::True(fHasExtendedPrefix, message);
1068
}
1069
else
1070
{
1057
- Assert::False(fRooted, message);
1071
+ Assert::False(fHasExtendedPrefix, message);
1072
}
1073
+ }
1074
1060
- message = String::Format("HasLongPathPrefix: {0}", gcnew String(wzPath));
1061
- if (fExpectedHasPrefix)
1075
+ void ValidateFullyQualifiedPath(LPCWSTR wzPath, BOOL fExpected)
1076
+ {
1077
+ BOOL fRooted = PathIsFullyQualified(wzPath);
1078
+ String^ message = String::Format("IsFullyQualified: {0}", gcnew String(wzPath));
1079
+ if (fExpected)
1080
{
1063
- Assert::True(fHasLongPathPrefix, message);
1081
+ Assert::True(fRooted, message);
1082
}
1083
else
1084
{
1067
- Assert::False(fHasLongPathPrefix, message);
1085
+ Assert::False(fRooted, message);
1086
}
1087
}
1088