[MSVCRT:APITEST]
[reactos.git] / rostests / apitests / msvcrt / CommandLine.c
1 /*
2 * PROJECT: ReactOS API Tests
3 * LICENSE: GPLv2+ - See COPYING in the top level directory
4 * PURPOSE: Test for CRT command-line handling.
5 * PROGRAMMER: Hermès BÉLUSCA - MAÏTO <hermes.belusca@sfr.fr>
6 */
7
8 #define WIN32_NO_STATUS
9 #define UNICODE
10 #include <stdio.h>
11 #include <wine/test.h>
12 #include <ndk/ntndk.h>
13
14 #include "./CmdLineUtil/CmdLineUtil.h"
15
16 #define COUNT_OF(x) (sizeof((x))/sizeof((x)[0]))
17
18 /**
19 * Extracts the command tail from the command line
20 * (deletes the program's name and keep the rest).
21 **/
22 #define SPACECHAR L' '
23 #define DQUOTECHAR L'"'
24
25 LPWSTR ExtractCmdLine(IN LPWSTR lpszCommandLine)
26 {
27 BOOL inDoubleQuote = FALSE;
28
29 /*
30 * Skip the program's name (the first token in the command line).
31 * Handle quoted program's name.
32 */
33 if (lpszCommandLine)
34 {
35 while ( (*lpszCommandLine > SPACECHAR) ||
36 (*lpszCommandLine && inDoubleQuote) )
37 {
38 if (*lpszCommandLine == DQUOTECHAR)
39 inDoubleQuote = !inDoubleQuote;
40
41 ++lpszCommandLine;
42 }
43
44 /* Skip all white spaces preceeding the second token. */
45 while (*lpszCommandLine && (*lpszCommandLine <= SPACECHAR))
46 ++lpszCommandLine;
47 }
48
49 return lpszCommandLine;
50 }
51
52 VOID ExtractCmdLine_U(IN OUT PUNICODE_STRING pCommandLine_U)
53 {
54 BOOL inDoubleQuote = FALSE;
55 PWSTR lpszCommandLine;
56
57 /*
58 * Skip the program's name (the first token in the command line).
59 * Handle quoted program's name.
60 */
61 if (pCommandLine_U && pCommandLine_U->Buffer && (pCommandLine_U->Length != 0))
62 {
63 lpszCommandLine = pCommandLine_U->Buffer;
64
65 while ( (pCommandLine_U->Length > 0) &&
66 ( (*lpszCommandLine > SPACECHAR) ||
67 (*lpszCommandLine && inDoubleQuote) ) )
68 {
69 if (*lpszCommandLine == DQUOTECHAR)
70 inDoubleQuote = !inDoubleQuote;
71
72 ++lpszCommandLine;
73 pCommandLine_U->Length -= sizeof(WCHAR);
74 }
75
76 /* Skip all white spaces preceeding the second token. */
77 while ((pCommandLine_U->Length > 0) && *lpszCommandLine && (*lpszCommandLine <= SPACECHAR))
78 {
79 ++lpszCommandLine;
80 pCommandLine_U->Length -= sizeof(WCHAR);
81 }
82
83 pCommandLine_U->Buffer = lpszCommandLine;
84 }
85
86 return;
87 }
88
89 /******************************************************************************/
90
91 /* The path to the utility program run by this test. */
92 static WCHAR UtilityProgramDirectory[MAX_PATH];
93
94 /* The list of tests. */
95 typedef struct _TEST_CASE
96 {
97 LPWSTR CmdLine;
98 BOOL bEncloseProgramNameInQuotes;
99 } TEST_CASE, *PTEST_CASE;
100
101 static TEST_CASE TestCases[] =
102 {
103 {L"", FALSE},
104 {L"foo bar", FALSE},
105 {L"\"foo bar\"", FALSE},
106 {L"foo \"bar John\" Doe", FALSE},
107
108 {L"", TRUE},
109 {L"foo bar", TRUE},
110 {L"\"foo bar\"", TRUE},
111 {L"foo \"bar John\" Doe", TRUE},
112 };
113
114 static void Test_CommandLine(IN ULONG TestNumber,
115 IN PTEST_CASE TestCase)
116 {
117 BOOL bRet;
118
119 BOOL bWasntInQuotes = (UtilityProgramDirectory[0] != L'"');
120 WCHAR CmdLine[MAX_PATH] = L"";
121 STARTUPINFOW si;
122 PROCESS_INFORMATION pi;
123
124 ZeroMemory(&si, sizeof(si));
125 ZeroMemory(&pi, sizeof(pi));
126 si.cb = sizeof(si);
127
128
129 /* Initialize the command line. */
130 if (TestCase->bEncloseProgramNameInQuotes && bWasntInQuotes)
131 wcscpy(CmdLine, L"\"");
132
133 wcscat(CmdLine, UtilityProgramDirectory);
134
135 if (TestCase->bEncloseProgramNameInQuotes && bWasntInQuotes)
136 wcscat(CmdLine, L"\"");
137
138 wcscat(CmdLine, L" ");
139 wcscat(CmdLine, TestCase->CmdLine);
140
141
142 /*
143 * Launch the utility program and wait till it's terminated.
144 */
145 bRet = CreateProcessW(NULL,
146 CmdLine,
147 NULL, NULL,
148 FALSE,
149 CREATE_UNICODE_ENVIRONMENT,
150 NULL, NULL,
151 &si, &pi);
152 ok(bRet, "Test %lu - Failed to launch ' %S ', error = %lu.\n", TestNumber, CmdLine, GetLastError());
153
154 if (bRet)
155 {
156 /* Wait until child process exits. */
157 WaitForSingleObject(pi.hProcess, INFINITE);
158
159 /* Close process and thread handles. */
160 CloseHandle(pi.hThread);
161 CloseHandle(pi.hProcess);
162 }
163
164 /*
165 * Analyses the result.
166 */
167 {
168 /* Open the data file. */
169 HANDLE hFile = CreateFileW(DATAFILE,
170 GENERIC_READ,
171 0, NULL,
172 OPEN_EXISTING,
173 FILE_ATTRIBUTE_NORMAL,
174 NULL);
175 ok(hFile != INVALID_HANDLE_VALUE, "Test %lu - Failed to open the data file 'C:\\cmdline.dat', error = %lu.\n", TestNumber, GetLastError());
176
177 if (hFile != INVALID_HANDLE_VALUE)
178 {
179 WCHAR BuffWinMain[MAX_PATH]; LPWSTR WinMainCmdLine = BuffWinMain;
180 WCHAR BuffWin32[MAX_PATH] ; LPWSTR Win32CmdLine = BuffWin32 ;
181 WCHAR BuffNT[0xffff /* Maximum USHORT size */];
182 UNICODE_STRING NTCmdLine;
183
184 DWORD dwSize, dwStringSize;
185
186 /*
187 * Format of the data file :
188 *
189 * [size_of_string 4 bytes][null_terminated_C_string]
190 * [size_of_string 4 bytes][null_terminated_C_string]
191 * [UNICODE_STRING_structure][string_buffer_of_UNICODE_STRING]
192 */
193
194 /* 1- Read the WinMain's command line. */
195 dwStringSize = 0;
196
197 ReadFile(hFile,
198 &dwStringSize,
199 sizeof(dwStringSize),
200 &dwSize,
201 NULL);
202
203 dwStringSize = min(dwStringSize, sizeof(BuffWinMain));
204 ReadFile(hFile,
205 WinMainCmdLine,
206 dwStringSize,
207 &dwSize,
208 NULL);
209 *(LPWSTR)((ULONG_PTR)WinMainCmdLine + dwStringSize) = 0;
210
211 /* 2- Read the Win32 mode command line. */
212 dwStringSize = 0;
213
214 ReadFile(hFile,
215 &dwStringSize,
216 sizeof(dwStringSize),
217 &dwSize,
218 NULL);
219
220 dwStringSize = min(dwStringSize, sizeof(BuffWin32));
221 ReadFile(hFile,
222 Win32CmdLine,
223 dwStringSize,
224 &dwSize,
225 NULL);
226 *(LPWSTR)((ULONG_PTR)Win32CmdLine + dwStringSize) = 0;
227
228 /* 3- Finally, read the UNICODE_STRING command line. */
229 ReadFile(hFile,
230 &NTCmdLine,
231 sizeof(NTCmdLine),
232 &dwSize,
233 NULL);
234
235 NTCmdLine.Buffer = BuffNT;
236 ReadFile(hFile,
237 NTCmdLine.Buffer,
238 NTCmdLine.Length,
239 &dwSize,
240 NULL);
241
242 /* Now close the file. */
243 CloseHandle(hFile);
244
245 /*
246 * Remove the program's name in the Win32 and NT command lines.
247 */
248 Win32CmdLine = ExtractCmdLine(Win32CmdLine);
249 ExtractCmdLine_U(&NTCmdLine);
250
251 /* Print the results */
252 /*
253 *(LPWSTR)((ULONG_PTR)NTCmdLine.Buffer + NTCmdLine.Length) = 0;
254 printf("WinMain cmdline = '%S'\n"
255 "Win32 cmdline = '%S'\n"
256 "NT cmdline = '%S'\n"
257 "NT length = %u\n",
258 WinMainCmdLine,
259 Win32CmdLine,
260 NTCmdLine.Buffer, NTCmdLine.Length);
261 */
262
263 /*
264 * Now check the results.
265 */
266 dwStringSize = min(wcslen(WinMainCmdLine), wcslen(Win32CmdLine));
267 ok(wcslen(WinMainCmdLine) == wcslen(Win32CmdLine), "Test %lu - WinMain and Win32 command lines do not have the same length !\n", TestNumber);
268 ok(wcsncmp(WinMainCmdLine, Win32CmdLine, dwStringSize) == 0, "Test %lu - WinMain and Win32 command lines are different !\n", TestNumber);
269
270 dwStringSize = min(wcslen(WinMainCmdLine), NTCmdLine.Length / sizeof(WCHAR));
271 ok(wcsncmp(WinMainCmdLine, NTCmdLine.Buffer, dwStringSize) == 0, "Test %lu - WinMain and NT command lines are different !\n", TestNumber);
272
273 dwStringSize = min(wcslen(Win32CmdLine), NTCmdLine.Length / sizeof(WCHAR));
274 ok(wcsncmp(Win32CmdLine, NTCmdLine.Buffer, dwStringSize) == 0, "Test %lu - Win32 and NT command lines are different !\n", TestNumber);
275 }
276 }
277
278 /*
279 * Always delete the data file.
280 */
281 DeleteFileW(DATAFILE);
282 }
283
284 START_TEST(CommandLine)
285 {
286 ULONG i;
287
288 DWORD dwError;
289 LPWSTR p = NULL;
290
291
292 /*
293 * Initialize the UtilityProgramDirectory variable.
294 */
295 GetModuleFileNameW(NULL, UtilityProgramDirectory, COUNT_OF(UtilityProgramDirectory));
296 dwError = GetLastError();
297 ok(dwError == ERROR_SUCCESS, "ERROR: Cannot retrieve the path to the current running process, last error %lu\n", dwError);
298 if (dwError != ERROR_SUCCESS) return;
299
300 /* Path : executable.exe or "executable.exe" or C:\path\executable.exe or "C:\path\executable.exe" */
301 p = wcsrchr(UtilityProgramDirectory, L'\\');
302 if (p && *p != 0)
303 *++p = 0; /* Null-terminate there : C:\path\ or "C:\path\ */
304 else
305 UtilityProgramDirectory[0] = 0; /* Suppress the executable.exe name */
306
307 wcscat(UtilityProgramDirectory, L"data\\CmdLineUtil.exe");
308
309 /* Close the opened quote if needed, and add a separating space */
310 if (UtilityProgramDirectory[0] == L'"') wcscat(UtilityProgramDirectory, L"\"");
311
312
313 /*
314 * Now launch the tests.
315 */
316 for (i = 0 ; i < COUNT_OF(TestCases) ; ++i)
317 {
318 Test_CommandLine(i, &TestCases[i]);
319 }
320 }
321
322 /* EOF */