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