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 
627