[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 #define COUNT_OF(x) (sizeof((x))/sizeof((x)[0]))
15
16 /*
17 * The path to the data file is hardcoded in cmdline_util.c
18 * Please synchronize it whenever you do a change.
19 */
20 #define DATAFILE L"C:\\cmdline.dat"
21
22 /**
23 * Extracts the command tail from the command line
24 * (deletes the program's name and keep the rest).
25 **/
26 #define SPACECHAR L' '
27 #define DQUOTECHAR L'\"'
28
29 LPWSTR ExtractCmdLine(IN LPWSTR lpszCommandLine)
30 {
31 BOOL inDoubleQuote = FALSE;
32
33 /*
34 * Skip the program's name (the first token in the command line).
35 * Handle quoted program's name.
36 */
37 if (lpszCommandLine)
38 {
39 while ( (*lpszCommandLine > SPACECHAR) ||
40 (*lpszCommandLine && inDoubleQuote) )
41 {
42 if (*lpszCommandLine == DQUOTECHAR)
43 inDoubleQuote = !inDoubleQuote;
44
45 ++lpszCommandLine;
46 }
47
48 /* Skip all white spaces preceeding the second token. */
49 while (*lpszCommandLine && (*lpszCommandLine <= SPACECHAR))
50 ++lpszCommandLine;
51 }
52
53 return lpszCommandLine;
54 }
55
56 VOID ExtractCmdLine_U(IN OUT PUNICODE_STRING pCommandLine_U)
57 {
58 BOOL inDoubleQuote = FALSE;
59 PWSTR lpszCommandLine;
60
61 /*
62 * Skip the program's name (the first token in the command line).
63 * Handle quoted program's name.
64 */
65 if (pCommandLine_U && pCommandLine_U->Buffer && (pCommandLine_U->Length != 0))
66 {
67 lpszCommandLine = pCommandLine_U->Buffer;
68
69 while ( (pCommandLine_U->Length > 0) &&
70 ( (*lpszCommandLine > SPACECHAR) ||
71 (*lpszCommandLine && inDoubleQuote) ) )
72 {
73 if (*lpszCommandLine == DQUOTECHAR)
74 inDoubleQuote = !inDoubleQuote;
75
76 ++lpszCommandLine;
77 pCommandLine_U->Length -= sizeof(WCHAR);
78 }
79
80 /* Skip all white spaces preceeding the second token. */
81 while ((pCommandLine_U->Length > 0) && *lpszCommandLine && (*lpszCommandLine <= SPACECHAR))
82 {
83 ++lpszCommandLine;
84 pCommandLine_U->Length -= sizeof(WCHAR);
85 }
86
87 pCommandLine_U->Buffer = lpszCommandLine;
88 }
89
90 return;
91 }
92
93
94 typedef struct _TEST_CASE
95 {
96 LPWSTR CmdLine;
97 } TEST_CASE, *PTEST_CASE;
98
99 static TEST_CASE TestCases[] =
100 {
101 {L"cmdline_util.exe"},
102 {L"cmdline_util.exe foo bar"},
103 {L"cmdline_util.exe \"foo bar\""},
104 {L"cmdline_util.exe foo \"bar John\" Doe"},
105
106 {L"\"cmdline_util.exe\""},
107 {L"\"cmdline_util.exe\" foo bar"},
108 {L"\"cmdline_util.exe\" \"foo bar\""},
109 {L"\"cmdline_util.exe\" foo \"bar John\" Doe"},
110
111 {L"\"cmdline_util.exe\""},
112 {L"\"cmdline_util.exe \"foo bar\"\""},
113 };
114
115 static void Test_CommandLine(IN ULONG TestNumber,
116 IN PTEST_CASE TestCase)
117 {
118 BOOL bRet;
119
120 WCHAR CmdLine[MAX_PATH];
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 wcscpy(CmdLine, TestCase->CmdLine);
129
130 /*
131 * Launch the utility program and wait till it's terminated.
132 */
133 bRet = CreateProcessW(NULL,
134 CmdLine,
135 NULL, NULL,
136 FALSE,
137 CREATE_UNICODE_ENVIRONMENT,
138 NULL, NULL,
139 &si, &pi);
140 ok(bRet, "Test %lu - Failed to launch ' %S ', error = %lu.\n", TestNumber, TestCase->CmdLine, GetLastError());
141
142 if (bRet)
143 {
144 /* Wait until child process exits. */
145 WaitForSingleObject(pi.hProcess, INFINITE);
146
147 /* Close process and thread handles. */
148 CloseHandle(pi.hThread);
149 CloseHandle(pi.hProcess);
150 }
151
152 /*
153 * Analyses the result.
154 */
155 {
156 /* Open the data file. */
157 HANDLE hFile = CreateFileW(DATAFILE,
158 GENERIC_READ,
159 0, NULL,
160 OPEN_EXISTING,
161 FILE_ATTRIBUTE_NORMAL,
162 NULL);
163 ok(hFile != INVALID_HANDLE_VALUE, "Test %lu - Failed to open the data file 'C:\\cmdline.dat', error = %lu.\n", TestNumber, GetLastError());
164
165 if (hFile != INVALID_HANDLE_VALUE)
166 {
167 WCHAR BuffWinMain[MAX_PATH]; LPWSTR WinMainCmdLine = BuffWinMain;
168 WCHAR BuffWin32[MAX_PATH] ; LPWSTR Win32CmdLine = BuffWin32 ;
169 WCHAR BuffNT[0xffff /* Maximum USHORT size */];
170 UNICODE_STRING NTCmdLine;
171
172 DWORD dwSize, dwStringSize;
173
174 /*
175 * Format of the data file :
176 *
177 * [size_of_string 4 bytes][null_terminated_C_string]
178 * [size_of_string 4 bytes][null_terminated_C_string]
179 * [UNICODE_STRING_structure][string_buffer_of_UNICODE_STRING]
180 */
181
182 /* 1- Read the WinMain's command line. */
183 dwStringSize = 0;
184
185 ReadFile(hFile,
186 &dwStringSize,
187 sizeof(dwStringSize),
188 &dwSize,
189 NULL);
190
191 dwStringSize = min(dwStringSize, sizeof(BuffWinMain));
192 ReadFile(hFile,
193 WinMainCmdLine,
194 dwStringSize,
195 &dwSize,
196 NULL);
197 *(LPWSTR)((ULONG_PTR)WinMainCmdLine + dwStringSize) = 0;
198
199 /* 2- Read the Win32 mode command line. */
200 dwStringSize = 0;
201
202 ReadFile(hFile,
203 &dwStringSize,
204 sizeof(dwStringSize),
205 &dwSize,
206 NULL);
207
208 dwStringSize = min(dwStringSize, sizeof(BuffWin32));
209 ReadFile(hFile,
210 Win32CmdLine,
211 dwStringSize,
212 &dwSize,
213 NULL);
214 *(LPWSTR)((ULONG_PTR)Win32CmdLine + dwStringSize) = 0;
215
216 /* 3- Finally, read the UNICODE_STRING command line. */
217 ReadFile(hFile,
218 &NTCmdLine,
219 sizeof(NTCmdLine),
220 &dwSize,
221 NULL);
222
223 NTCmdLine.Buffer = BuffNT;
224 ReadFile(hFile,
225 NTCmdLine.Buffer,
226 NTCmdLine.Length,
227 &dwSize,
228 NULL);
229
230 /* Now close the file. */
231 CloseHandle(hFile);
232
233 /*
234 * Remove the program's name in the Win32 and NT command lines.
235 */
236 Win32CmdLine = ExtractCmdLine(Win32CmdLine);
237 ExtractCmdLine_U(&NTCmdLine);
238
239 /* Print the results */
240 *(LPWSTR)((ULONG_PTR)NTCmdLine.Buffer + NTCmdLine.Length) = 0;
241 printf("WinMain cmdline = '%S'\n"
242 "Win32 cmdline = '%S'\n"
243 "NT cmdline = '%S'\n"
244 "NT length = %u\n",
245 WinMainCmdLine,
246 Win32CmdLine,
247 NTCmdLine.Buffer, NTCmdLine.Length);
248
249 /*
250 * Now check the results.
251 */
252 dwStringSize = min(wcslen(WinMainCmdLine), wcslen(Win32CmdLine));
253 ok(wcslen(WinMainCmdLine) == wcslen(Win32CmdLine), "Test %lu - WinMain and Win32 command lines do not have the same length !\n", TestNumber);
254 ok(wcsncmp(WinMainCmdLine, Win32CmdLine, dwStringSize) == 0, "Test %lu - WinMain and Win32 command lines are different !\n", TestNumber);
255
256 dwStringSize = min(wcslen(WinMainCmdLine), NTCmdLine.Length / sizeof(WCHAR));
257 ok(wcsncmp(WinMainCmdLine, NTCmdLine.Buffer, dwStringSize) == 0, "Test %lu - WinMain and NT command lines are different !\n", TestNumber);
258
259 dwStringSize = min(wcslen(Win32CmdLine), NTCmdLine.Length / sizeof(WCHAR));
260 ok(wcsncmp(Win32CmdLine, NTCmdLine.Buffer, dwStringSize) == 0, "Test %lu - Win32 and NT command lines are different !\n", TestNumber);
261 }
262 }
263
264 /*
265 * Always delete the data file.
266 */
267 DeleteFileW(DATAFILE);
268 }
269
270 START_TEST(CommandLine)
271 {
272 ULONG i;
273
274 for (i = 0 ; i < COUNT_OF(TestCases) ; ++i)
275 {
276 Test_CommandLine(i, &TestCases[i]);
277 }
278 }
279
280 /* EOF */