[NTDLL_APITEST]
[reactos.git] / rostests / apitests / ntdll / RtlDosPathNameToNtPathName_U.c
1 /*
2 * PROJECT: ReactOS api tests
3 * LICENSE: GPLv2+ - See COPYING in the top level directory
4 * PURPOSE: Test for RtlDosPathNameToNtPathName_U
5 * PROGRAMMER: Mike "tamlin" Nordell
6 */
7 /* TODO:
8 * - Make the code UNICODE aware. If a user is inside a directory with
9 * non-ANSI characters somewhere in the path, all bets are currently off.
10 * - Remove hard-coded path size limits.
11 * - Un-tabify to match style of other code.
12 * - Clean up cruft. Probably remove the option of running it stand-alone.
13 */
14
15 /* Test to see that ntdll.RtlDosPathNameToNtPathName_U behaves _exactly_
16 * like Windows with all possible input to it.
17 * - relative path.
18 * - absolute paths
19 * - \\.\C:\foo
20 * - \\.\C:\foo\
21 * - \\?\C:\foo
22 * - \\?\C:\foo\
23 * - \??\C:
24 * - \??\C:\
25 *
26 * Caveat: The "\??\*" form behaves different depending on Windows version.
27 * Some tests will fail if there is no C: volume.
28 *
29 * Code is assumed to be compiled as 32-bit "ANSI" (i.e. with _UNICODE) undefined.
30 */
31
32 // Enable this define to compile the test as a ReactOS auto-test.
33 // Disable it to compile on plain Win32, to test-run and get info.
34 #define COMPILE_AS_ROSTEST
35
36 #ifndef COMPILE_AS_ROSTEST
37 # define PRINT_INFO // Also print, in addition to testing
38 # include <windows.h>
39 # include <stdio.h>
40 # include <stddef.h>
41 #else /* Compile for ReactOS or wine */
42 # include <apitest.h>
43 # define WIN32_NO_STATUS
44 # include <stdio.h>
45 # include <ndk/rtlfuncs.h>
46 #endif
47
48 /*
49 BOOLEAN
50 NTAPI
51 RtlDosPathNameToNtPathName_U(IN PCWSTR DosName,
52 OUT PUNICODE_STRING NtName,
53 OUT PCWSTR *PartName,
54 OUT PRTL_RELATIVE_NAME_U RelativeName)
55 */
56
57 #ifndef COMPILE_AS_ROSTEST
58
59 typedef struct UNICODE_STRING {
60 USHORT Length;
61 USHORT MaximumLength;
62 PWSTR Buffer;
63 } UNICODE_STRING, *PUNICODE_STRING;
64
65 typedef struct _RTLP_CURDIR_REF
66 {
67 LONG RefCount;
68 HANDLE Handle;
69 } RTLP_CURDIR_REF, *PRTLP_CURDIR_REF;
70
71 typedef struct RTL_RELATIVE_NAME_U {
72 UNICODE_STRING RelativeName;
73 HANDLE ContainingDirectory;
74 PRTLP_CURDIR_REF CurDirRef;
75 } RTL_RELATIVE_NAME_U, *PRTL_RELATIVE_NAME_U;
76
77 typedef BOOLEAN (__stdcall *RtlDosPathNameToNtPathName_U_t)(PCWSTR,PUNICODE_STRING,PCWSTR*,PRTL_RELATIVE_NAME_U);
78
79 static RtlDosPathNameToNtPathName_U_t RtlDosPathNameToNtPathName_U;
80
81 #endif // !COMPILE_AS_ROSTEST
82
83
84 static void check_result(BOOLEAN bOK, const char* pszErr)
85 {
86 #ifdef COMPILE_AS_ROSTEST
87 ok(bOK, "%s.\n", pszErr);
88 #else
89 if (!bOK) {
90 printf("\a** %s.\n", pszErr);
91 }
92 #endif
93 }
94
95
96 #ifndef COMPILE_AS_ROSTEST
97 static void prucs(const char* pszDesc, UNICODE_STRING* pucs)
98 {
99 WCHAR wszTmp[512];
100 memcpy(wszTmp, pucs->Buffer, pucs->Length);
101 wszTmp[pucs->Length/2] = 0;
102 printf("%-12s: \"%S\"\n", pszDesc, wszTmp);
103 }
104 #endif
105
106 // Test RtlDosPathNameToNtPathName_U .
107 // pwszExpected shall contain the expected result for NtName
108 // *without* the leading "\??\".
109 // pwszExpectedPartName shall contain the expected result for PartName.
110 // NULL Expected means result is expected to be NULL too.
111 static void test2(LPCWSTR pwsz, LPCWSTR pwszExpected, LPCWSTR pwszExpectedPartName)
112 {
113 UNICODE_STRING NtName;
114 PWSTR PartName;
115 RTL_RELATIVE_NAME_U RelativeName;
116 BOOLEAN bOK;
117
118 bOK = RtlDosPathNameToNtPathName_U(pwsz, &NtName, (PCWSTR*)&PartName, &RelativeName);
119
120 check_result(bOK, "RtlDosPathNameToNtPathName_U failed");
121 if (!bOK) {
122 printf("input: \"%S\"\n", pwsz);
123 return;
124 }
125
126 #if !defined(COMPILE_AS_ROSTEST) && defined(PRINT_INFO)
127 printf("--------------------------\n");
128 printf("in : \"%S\"\n", pwsz);
129 prucs("NtName", &NtName);
130 printf("PartName : \"%S\"\n", PartName ? PartName : L"(null)");
131 // prucs("RelativeName", &RelativeName.RelativeName);
132 #endif
133
134 // Disregarding input, output (NtName) shall always start with "\??\".
135 bOK = NtName.Length >= 8 &&
136 memcmp(NtName.Buffer, L"\\??\\", 8) == 0;
137 check_result(bOK, "NtName does not start with \"\\??\\\"");
138 if (!bOK) {
139 return;
140 }
141
142 if (pwszExpected) {
143 PWSTR pwszActual = NtName.Buffer + 4;
144 const size_t lenExp = wcslen(pwszExpected);
145 const size_t lenAct = (NtName.Length - 8) / 2;
146 bOK = (lenExp == lenAct) &&
147 memcmp(pwszActual, pwszExpected, lenExp * 2) == 0;
148 check_result(bOK, "NtName does not match expected");
149 if (!bOK)
150 {
151 printf("input: : %2u chars \"%S\"\n", wcslen(pwsz), pwsz);
152 printf("Expected: %2u chars \"%S\"\n", lenExp, pwszExpected);
153 printf("Actual : %2u chars \"%S\"\n", lenAct, lenAct ? pwszActual : L"(null)");
154 return;
155 }
156 } else
157 if (NtName.Length)
158 {
159 PWSTR pwszActual = NtName.Buffer + 4;
160 const size_t lenAct = (NtName.Length - 8) / 2;
161 check_result(FALSE, "Unexpected NtName (expected NULL)");
162 printf("input: : %2u chars \"%S\"\n", wcslen(pwsz), pwsz);
163 printf("Actual : %2u chars \"%S\"\n", lenAct, pwszActual);
164 }
165
166 if (pwszExpectedPartName) {
167 const size_t lenExp = wcslen(pwszExpectedPartName);
168 const size_t lenAct = PartName ? wcslen(PartName) : 0;
169 bOK = (lenExp == lenAct) &&
170 wcscmp(PartName, pwszExpectedPartName) == 0;
171 check_result(bOK, "PartName does not match expected");
172 if (!bOK) {
173 printf("input: : %2u chars \"%S\"\n", wcslen(pwsz), pwsz);
174 printf("Expected: %2u chars \"%S\"\n", lenExp, pwszExpectedPartName);
175 printf("Actual : %2u chars \"%S\"\n", lenAct, lenAct ? PartName : L"(null)");
176 return;
177 }
178 } else
179 if (PartName)
180 {
181 check_result(FALSE, "Unexpected PartName (expected NULL).");
182 printf("input: : %2u chars \"%S\"\n", wcslen(pwsz), pwsz);
183 printf("Actual : %2u chars %S\n", wcslen(PartName), PartName);
184 }
185 }
186
187 // NULL Expected means result is expected to be NULL too.
188 static void test(const char* psz, const char* pszExpected, const char* pszExpectedPartName)
189 {
190 WCHAR wszTmp1[512];
191 WCHAR wszTmp2[512];
192 WCHAR wszTmp3[512];
193 LPCWSTR p2 = 0;
194 LPCWSTR p3 = 0;
195 swprintf(wszTmp1, L"%S", psz);
196 if (pszExpected) {
197 swprintf(wszTmp2, L"%S", pszExpected);
198 p2 = wszTmp2;
199 }
200 if (pszExpectedPartName) {
201 swprintf(wszTmp3, L"%S", pszExpectedPartName);
202 p3 = wszTmp3;
203 }
204 test2(wszTmp1, p2, p3);
205 }
206
207
208 typedef struct DirComponents
209 {
210 char szCD[512];
211 char szCDPlusSlash[512];
212 char* pszLastCDComponent;
213 char szCurDrive[4];
214 char szCurDriveSlash[4];
215 char szParentDir[512];
216 char szParentDirPlusSlash[512];
217 char szNextLastCDComponent[260];
218 const char* pszNextLastCDComponent;
219 const char* pszPD; // parent dir
220 const char* pszPDPlusSlash;
221 } DirComponents;
222
223
224 static void InitDirComponents(DirComponents* p)
225 {
226 /* While the following code seems to work, it's an unholy mess
227 * and should probably be cleaned up.
228 */
229 BOOLEAN bOK;
230
231 p->pszNextLastCDComponent = 0;
232 p->pszPD = 0;
233 p->pszPDPlusSlash = 0;
234
235 GetCurrentDirectory(sizeof(p->szCD) / sizeof(*p->szCD), p->szCD);
236
237 bOK = strlen(p->szCD) >= 2 && p->szCD[1] == ':';
238 check_result(bOK, "Expected curdir to be a drive letter. It's not");
239
240 if (!bOK) {
241 printf("Curdir is \"%s\"\n", p->szCD);
242 exit(1);
243 }
244
245 bOK = p->szCD[2] == '\\';
246 check_result(bOK, "CD is missing a slash as its third character");
247 if (!bOK) {
248 printf("CD is \"%s\"\n", p->szCD);
249 exit(1);
250 }
251
252 // Note that if executed from the root directory, a backslash is
253 // already appended.
254 strcpy(p->szCDPlusSlash, p->szCD);
255 if (strlen(p->szCD) > 3) {
256 // Append trailing backslash
257 strcat(p->szCDPlusSlash, "\\");
258 }
259
260 memcpy(p->szCurDrive, p->szCD, 2);
261 p->szCurDrive[2] = 0;
262 sprintf(p->szCurDriveSlash, "%s\\", p->szCurDrive);
263
264 p->pszLastCDComponent = strrchr(p->szCD, '\\');
265 if (p->pszLastCDComponent)
266 {
267 // We have a parent directory
268 memcpy(p->szParentDir, p->szCD, p->pszLastCDComponent - p->szCD);
269 p->szParentDir[p->pszLastCDComponent - p->szCD] = 0;
270 p->pszPD = p->szParentDir;
271 if (strlen(p->szParentDir) == 2 && p->szParentDir[1] == ':') {
272 // When run from root directory, this is expected to
273 // have a trailing backslash
274 strcat(p->szParentDir, "\\");
275 }
276 strcpy(p->szParentDirPlusSlash, p->szParentDir);
277 if (p->szParentDirPlusSlash[strlen(p->szParentDirPlusSlash)-1] != '\\') {
278 strcat(p->szParentDirPlusSlash, "\\");
279 }
280 p->pszPDPlusSlash = p->szParentDirPlusSlash;
281
282 // Check if we have a directory above that too.
283 *p->pszLastCDComponent = 0;
284 p->pszNextLastCDComponent = strrchr(p->szCD, '\\');
285 *p->pszLastCDComponent = '\\';
286 if (p->pszNextLastCDComponent) {
287 ++p->pszNextLastCDComponent; // skip the leading slash
288 if (p->pszNextLastCDComponent + 1 >= p->pszLastCDComponent) {
289 p->pszNextLastCDComponent = 0;
290 } else {
291 const size_t siz = p->pszLastCDComponent - p->pszNextLastCDComponent;
292 memcpy(p->szNextLastCDComponent, p->pszNextLastCDComponent, siz);
293 p->szNextLastCDComponent[siz] = 0;
294 p->pszNextLastCDComponent = p->szNextLastCDComponent;
295 }
296 }
297 }
298 if (p->pszLastCDComponent && p->pszLastCDComponent[1] == 0) {
299 // If the backslash is the last character in the path,
300 // this is a NULL-component.
301 p->pszLastCDComponent = 0;
302 } else {
303 ++p->pszLastCDComponent; // skip the leading slash
304 }
305 }
306
307
308 #ifndef COMPILE_AS_ROSTEST
309 static void InitFunctionPointer()
310 {
311 HINSTANCE hDll = LoadLibrary("ntdll");
312 if (!hDll) {
313 printf("Major failure. Couldn't even load ntdll!\n");
314 exit(1);
315 }
316 RtlDosPathNameToNtPathName_U =
317 (RtlDosPathNameToNtPathName_U_t)GetProcAddress(hDll, "RtlDosPathNameToNtPathName_U");
318 if (!RtlDosPathNameToNtPathName_U) {
319 printf("Major failure. Couldn't get RtlDosPathNameToNtPathName_U!\n");
320 exit(1);
321 }
322 }
323
324 # if defined(PRINT_INFO)
325 static DWORD get_win_ver()
326 {
327 # ifdef COMPILE_AS_ROSTEST
328 PPEB Peb = NtCurrentPeb();
329 const DWORD dwWinVer = (DWORD)(Peb->OSMinorVersion << 8) | Peb->OSMajorVersion;
330 # else
331 const DWORD dwWinVer = GetVersion();
332 # endif
333 return dwWinVer;
334 }
335 # endif /* PRINT_INFO */
336 #endif /* !COMPILE_AS_ROSTEST */
337
338
339 #ifdef COMPILE_AS_ROSTEST
340 START_TEST(RtlDosPathNameToNtPathName_U)
341 #else
342 int main()
343 #endif
344 {
345 #if defined(PRINT_INFO)
346 const DWORD dwWinVer = get_win_ver();
347 const BYTE WinVerMaj = (BYTE)dwWinVer;
348 const BYTE WinVerMin = HIBYTE(LOWORD(dwWinVer));
349 #endif // PRINT_INFO
350
351 DirComponents cd;
352 char szTmp[512];
353
354 #ifndef COMPILE_AS_ROSTEST
355 InitFunctionPointer();
356 #endif
357
358 InitDirComponents(&cd);
359
360 #if defined(PRINT_INFO)
361 printf("WinVer: %d.%d\n", WinVerMaj, WinVerMin);
362 printf("pszLastCDComponent \"%s\"\n", cd.pszLastCDComponent);
363 printf("pszNextLastCDComponent \"%s\"\n", cd.pszNextLastCDComponent);
364 printf("pszParentDir \"%s\"\n", cd.pszPD);
365 printf("pszParentDirPlusSlash \"%s\"\n", cd.pszPDPlusSlash);
366 #endif
367
368 #define PREP0 /* The normal Win32 namespace. Fully parsed. */
369 #define PREP1 "\\\\.\\" /* The Win32 Device Namespace. Only partially parsed. */
370 #define PREP2 "\\\\?\\" /* The Win32 File Namespace. Only partially parsed. */
371
372 // input name NtName PartName
373 // volume-absolute paths
374 test(PREP1 "C:" , "C:" , "C:");
375 test(PREP2 "C:" , "C:" , "C:");
376 test(PREP0 "C:\\" , "C:\\" , NULL);
377 test(PREP1 "C:\\" , "C:\\" , NULL);
378 test(PREP2 "C:\\" , "C:\\" , NULL);
379 test(PREP0 "C:\\foo" , "C:\\foo" , "foo");
380 test(PREP1 "C:\\foo" , "C:\\foo" , "foo");
381 test(PREP2 "C:\\foo" , "C:\\foo" , "foo");
382 test(PREP0 "C:\\foo\\" , "C:\\foo\\" , NULL);
383 test(PREP1 "C:\\foo\\" , "C:\\foo\\" , NULL);
384 test(PREP2 "C:\\foo\\" , "C:\\foo\\" , NULL);
385 test(PREP0 "C:\\foo\\bar" , "C:\\foo\\bar" , "bar");
386 test(PREP1 "C:\\foo\\bar" , "C:\\foo\\bar" , "bar");
387 test(PREP2 "C:\\foo\\bar" , "C:\\foo\\bar" , "bar");
388 test(PREP0 "C:\\foo\\bar\\", "C:\\foo\\bar\\" , NULL);
389 test(PREP1 "C:\\foo\\bar\\", "C:\\foo\\bar\\" , NULL);
390 test(PREP2 "C:\\foo\\bar\\", "C:\\foo\\bar\\" , NULL);
391 test(PREP0 "C:\\foo\\.." , "C:\\" , NULL);
392 test(PREP1 "C:\\foo\\.." , "C:" , "C:");
393 test(PREP2 "C:\\foo\\.." , "C:\\foo\\.." , "..");
394 test(PREP0 "C:\\foo\\..\\" , "C:\\" , NULL);
395 test(PREP1 "C:\\foo\\..\\" , "C:\\" , NULL);
396 test(PREP2 "C:\\foo\\..\\" , "C:\\foo\\..\\" , NULL);
397 test(PREP0 "C:\\foo." , "C:\\foo" , "foo");
398 test(PREP1 "C:\\foo." , "C:\\foo" , "foo");
399 test(PREP2 "C:\\foo." , "C:\\foo." , "foo.");
400
401 test(PREP0 "C:\\f\\b\\.." , "C:\\f" , "f");
402 test(PREP1 "C:\\f\\b\\.." , "C:\\f" , "f");
403 test(PREP2 "C:\\f\\b\\.." , "C:\\f\\b\\.." , "..");
404 test(PREP0 "C:\\f\\b\\..\\", "C:\\f\\" , NULL);
405 test(PREP1 "C:\\f\\b\\..\\", "C:\\f\\" , NULL);
406 test(PREP2 "C:\\f\\b\\..\\", "C:\\f\\b\\..\\" , NULL);
407
408 // CD-relative paths
409
410 // RtlDosPathNameToNtPathName_U makes no distinction for
411 // special device names, such as "PhysicalDisk0", "HarddiskVolume0"
412 // or "Global??". They all follow the same pattern as a named
413 // filesystem entry, why they implicitly tested by the following
414 // "foo" and "foo\" cases.
415 sprintf(szTmp, "%s%s", cd.szCDPlusSlash, "foo");
416 test(PREP0 "foo" , szTmp , "foo");
417 test(PREP1 "foo" , "foo" , "foo");
418 test(PREP2 "foo" , "foo" , "foo");
419
420 sprintf(szTmp, "%s%s", cd.szCDPlusSlash , "foo\\");
421 test(PREP0 "foo\\" , szTmp , NULL);
422 test(PREP1 "foo\\" , "foo\\" , NULL);
423 test(PREP2 "foo\\" , "foo\\" , NULL);
424
425 test(PREP0 "." , cd.szCD , cd.pszLastCDComponent);
426 test(PREP1 "." , "" , NULL);
427 test(PREP2 "." , "." , ".");
428 test(PREP0 ".\\" , cd.szCDPlusSlash , NULL);
429 test(PREP1 ".\\" , "" , NULL);
430 test(PREP2 ".\\" , ".\\" , NULL);
431 test(PREP0 ".\\." , cd.szCD , cd.pszLastCDComponent);
432 test(PREP1 ".\\." , "" , NULL);
433 test(PREP2 ".\\." , ".\\." , ".");
434 test(PREP0 ".\\.." , cd.pszPD , cd.pszNextLastCDComponent);
435 test(PREP1 ".\\.." , "" , NULL);
436 test(PREP2 ".\\.." , ".\\.." , "..");
437 test(PREP0 ".." , cd.pszPD , cd.pszNextLastCDComponent);
438 test(PREP1 ".." , "" , NULL);
439 test(PREP2 ".." , ".." , "..");
440 test(PREP0 "..\\" , cd.pszPDPlusSlash , NULL);
441 test(PREP1 "..\\" , "" , NULL);
442 test(PREP2 "..\\" , "..\\" , NULL);
443 // malformed
444 test(PREP0 "..." , cd.szCDPlusSlash , NULL);
445 test(PREP1 "..." , "" , NULL);
446 test(PREP2 "..." , "..." , "...");
447
448 // Test well-known "special" DOS device names.
449 test(PREP0 "NUL" , "NUL" , NULL);
450 test(PREP1 "NUL" , "NUL" , "NUL");
451 test(PREP2 "NUL" , "NUL" , "NUL");
452 test(PREP0 "NUL:" , "NUL" , NULL);
453 test(PREP1 "NUL:" , "NUL:" , "NUL:");
454 test(PREP2 "NUL:" , "NUL:" , "NUL:");
455 test(PREP0 "CON" , "CON" , NULL);
456 // NOTE: RtlDosPathNameToNtPathName_U (as currently tested) fails for
457 // the input "\\.\CON" on two widely different Windows versions.
458 // test(PREP1 "CON" , "CON" , "CON");
459 test(PREP2 "CON" , "CON" , "CON");
460 test(PREP0 "CON:" , "CON" , NULL);
461 test(PREP1 "CON:" , "CON:" , "CON:");
462 test(PREP2 "CON:" , "CON:" , "CON:");
463
464 sprintf(szTmp, "%s\\%s", cd.szCD, "NUL:\\");
465 test(PREP0 "NUL:\\" , szTmp , NULL);
466 test(PREP1 "NUL:\\" , "NUL:\\" , NULL);
467 test(PREP2 "NUL:\\" , "NUL:\\" , NULL);
468 test(PREP0 "C:NUL" , "NUL" , NULL);
469 test(PREP1 "C:NUL" , "C:NUL" , "C:NUL");
470 test(PREP2 "C:NUL" , "C:NUL" , "C:NUL");
471 test(PREP0 "C:\\NUL" , "NUL" , NULL);
472 test(PREP1 "C:\\NUL" , "C:\\NUL" , "NUL");
473 test(PREP2 "C:\\NUL" , "C:\\NUL" , "NUL");
474 test(PREP0 "C:\\NUL\\" , "C:\\NUL\\" , NULL);
475 test(PREP1 "C:\\NUL\\" , "C:\\NUL\\" , NULL);
476 test(PREP2 "C:\\NUL\\" , "C:\\NUL\\" , NULL);
477
478 // root-paths
479 test(PREP0 "\\" , cd.szCurDriveSlash, NULL);
480 test(PREP1 "\\" , "" , NULL);
481 test(PREP2 "\\" , "\\" , NULL);
482
483 test(PREP0 "\\." , cd.szCurDriveSlash, NULL);
484 test(PREP1 "\\." , "" , NULL);
485 test(PREP2 "\\." , "\\." , ".");
486
487 test(PREP0 "\\.." , cd.szCurDriveSlash, NULL);
488 test(PREP1 "\\.." , "" , NULL);
489 test(PREP2 "\\.." , "\\.." , "..");
490
491 test(PREP0 "\\..." , cd.szCurDriveSlash, NULL);
492 test(PREP1 "\\..." , "" , NULL);
493 test(PREP2 "\\..." , "\\..." , "...");
494
495 // malformed
496 sprintf(szTmp, "%s%s", cd.szCurDrive, "\\C:");
497 test(PREP0 "\\C:" , szTmp , "C:");
498 test(PREP1 "\\C:" , "C:" , "C:");
499 test(PREP2 "\\C:" , "\\C:" , "C:");
500
501 sprintf(szTmp, "%s%s", cd.szCurDrive, "\\C:\\");
502 test(PREP0 "\\C:\\" , szTmp , NULL);
503 test(PREP1 "\\C:\\" , "C:\\" , NULL);
504 test(PREP2 "\\C:\\" , "\\C:\\" , NULL);
505
506 // UNC paths
507 test(PREP0 "\\\\" , "UNC\\" , NULL);
508 test(PREP1 "\\\\" , "" , NULL);
509 test(PREP2 "\\\\" , "\\\\" , NULL);
510 test(PREP0 "\\\\\\" , "UNC\\\\" , NULL);
511 test(PREP1 "\\\\\\" , "" , NULL);
512 test(PREP2 "\\\\\\" , "\\\\\\" , NULL);
513 test(PREP0 "\\\\foo" , "UNC\\foo" , NULL);
514 test(PREP1 "\\\\foo" , "foo" , "foo");
515 test(PREP2 "\\\\foo" , "\\\\foo" , "foo");
516
517 test(PREP0 "\\\\foo\\.." , "UNC\\foo\\" , NULL);
518 test(PREP1 "\\\\foo\\.." , "" , NULL);
519 test(PREP2 "\\\\foo\\.." , "\\\\foo\\.." , "..");
520
521 test(PREP0 "\\\\foo\\" , "UNC\\foo\\" , NULL);
522 test(PREP1 "\\\\foo\\" , "foo\\" , NULL);
523 test(PREP2 "\\\\foo\\" , "\\\\foo\\" , NULL);
524 test(PREP0 "\\\\f\\b" , "UNC\\f\\b" , NULL);
525 test(PREP1 "\\\\f\\b" , "f\\b" , "b");
526 test(PREP2 "\\\\f\\b" , "\\\\f\\b" , "b");
527 test(PREP0 "\\\\f\\b\\" , "UNC\\f\\b\\" , NULL);
528 test(PREP1 "\\\\f\\b\\" , "f\\b\\" , NULL);
529 test(PREP2 "\\\\f\\b\\" , "\\\\f\\b\\" , NULL);
530
531 test(PREP0 "\\\\f\\b\\.." , "UNC\\f\\b" , NULL);
532 test(PREP1 "\\\\f\\b\\.." , "f" , "f");
533 test(PREP2 "\\\\f\\b\\.." , "\\\\f\\b\\.." , "..");
534
535 // strange UNC-paths
536 test(PREP0 "\\\\C:" , "UNC\\C:" , NULL);
537 test(PREP1 "\\\\C:" , "C:" , "C:");
538 test(PREP2 "\\\\C:" , "\\\\C:" , "C:");
539 test(PREP0 "\\\\C:\\" , "UNC\\C:\\" , NULL);
540 test(PREP1 "\\\\C:\\" , "C:\\" , NULL);
541 test(PREP2 "\\\\C:\\" , "\\\\C:\\" , NULL);
542 test(PREP0 "\\\\NUL" , "UNC\\NUL" , NULL);
543 test(PREP1 "\\\\NUL" , "NUL" , "NUL");
544 test(PREP2 "\\\\NUL" , "\\\\NUL" , "NUL");
545 test(PREP0 "\\\\NUL:" , "UNC\\NUL:" , NULL);
546 test(PREP1 "\\\\NUL:" , "NUL:" , "NUL:");
547 test(PREP2 "\\\\NUL:" , "\\\\NUL:" , "NUL:");
548 test(PREP0 "\\\\NUL:\\" , "UNC\\NUL:\\" , NULL);
549 test(PREP1 "\\\\NUL:\\" , "NUL:\\" , NULL);
550 test(PREP2 "\\\\NUL:\\" , "\\\\NUL:\\" , NULL);
551
552 // UNC + forward slashes
553 test(PREP0 "//" , "UNC\\" , NULL);
554 test(PREP1 "//" , "" , NULL);
555 test(PREP2 "//" , "//" , "//");
556 test(PREP0 "//C:" , "UNC\\C:" , NULL);
557 test(PREP1 "//C:" , "C:" , "C:");
558 test(PREP2 "//C:" , "//C:" , "//C:");
559 test(PREP0 "//C:/" , "UNC\\C:\\" , NULL);
560 test(PREP1 "//C:/" , "C:\\" , NULL);
561 test(PREP2 "//C:/" , "//C:/" , "//C:/");
562 test(PREP0 "//." , "" , NULL);
563 test(PREP1 "//." , "" , NULL);
564 test(PREP2 "//." , "//." , "//.");
565 test(PREP0 "//.." , "UNC\\" , NULL);
566 test(PREP1 "//.." , "" , NULL);
567 test(PREP2 "//.." , "//.." , "//..");
568 test(PREP0 "/./" , cd.szCurDriveSlash, NULL);
569 test(PREP1 "/./" , "" , NULL);
570 test(PREP2 "/./" , "/./" , "/./");
571 test(PREP0 "//./" , "" , NULL);
572 test(PREP1 "//./" , "" , NULL);
573 test(PREP2 "//./" , "//./" , "//./");
574
575 test(cd.szCD , cd.szCD , cd.pszLastCDComponent);
576 test(cd.szCDPlusSlash , cd.szCDPlusSlash , NULL);
577
578 #if 0
579 // The following tests are "problematic", as they return results based on
580 // what your CD on C: is, whether or not you actually run the program
581 // from C:. For that reason, they are currently disabled.
582 test(PREP0 "C:" , "C:\\"+C_curdir , C_curdir);
583 test(PREP0 "C:NUL\\" , "C:\\"+C_curdir+"\\NUL\\" , NULL);
584 #endif
585
586 #if 0 // Disabled due to... see the comment inside the block.
587 {
588 char szExp[32];
589 BOOL bValid = FALSE;
590 char szPrepend[32];
591 szPrepend[0] = 0;
592 // Strictly speaking, this "Should Never Happen(tm)", calling
593 // RtlDosPathNameToNtPathName_U with a source already formed as
594 // a full NT name ("\??\"), why it's not the end of the world
595 // that this test is currently disabled.
596 //
597 // Some versions of Windows prepends driveletter + colon
598 // for the process' current volume.
599 // Prepending curdrive is most likely a bug that got fixed in
600 // later versions of Windows, but for compatibility it may
601 // become a requirement to "shim" this.
602 //
603 // Known operating systems prepending "Z:\??\" (assuming the
604 // process' CD is on the volume Z:):
605 // - XP sp2.
606 //
607 // Known operating systems not prepending:
608 // - Win7 64 (as 32-bit)
609 if (WinVerMaj == 5) {
610 sprintf(szPrepend, "%s\\??\\", cd.szCurDrive);
611 }
612
613 sprintf(szExp, "%s%s", szPrepend, "C:");
614 test("\\??\\C:", szExp, "C:");
615
616 sprintf(szExp, "%s%s", szPrepend, "C:\\");
617 test("\\??\\C:\\", szExp, NULL);
618
619 }
620 #endif
621
622 #ifndef COMPILE_AS_ROSTEST
623 return 0;
624 #endif
625 }
626