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 #include <apitest.h>
9 
10 #define WIN32_NO_STATUS
11 #include <stdio.h>
12 #include <ndk/umtypes.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     /* Add a separating space and copy the tested command line parameters. */
139     wcscat(CmdLine, L" ");
140     wcscat(CmdLine, TestCase->CmdLine);
141 
142 
143     /*
144      * Launch the utility program and wait till it's terminated.
145      */
146     bRet = CreateProcessW(NULL,
147                           CmdLine,
148                           NULL, NULL,
149                           FALSE,
150                           CREATE_UNICODE_ENVIRONMENT,
151                           NULL, NULL,
152                           &si, &pi);
153     ok(bRet, "Test %lu - Failed to launch ' %S ', error = %lu.\n", TestNumber, CmdLine, GetLastError());
154 
155     if (bRet)
156     {
157         /* Wait until child process exits. */
158         WaitForSingleObject(pi.hProcess, INFINITE);
159 
160         /* Close process and thread handles. */
161         CloseHandle(pi.hThread);
162         CloseHandle(pi.hProcess);
163     }
164 
165     /*
166      * Analyses the result.
167      */
168     {
169         /* Open the data file. */
170         HANDLE hFile = CreateFileW(DATAFILE,
171                                    GENERIC_READ,
172                                    0, NULL,
173                                    OPEN_EXISTING,
174                                    FILE_ATTRIBUTE_NORMAL,
175                                    NULL);
176         ok(hFile != INVALID_HANDLE_VALUE, "Test %lu - Failed to open the data file 'C:\\cmdline.dat', error = %lu.\n", TestNumber, GetLastError());
177 
178         if (hFile != INVALID_HANDLE_VALUE)
179         {
180             WCHAR BuffWinMain[MAX_PATH]; LPWSTR WinMainCmdLine = BuffWinMain;
181             WCHAR BuffWin32[MAX_PATH]  ; LPWSTR Win32CmdLine   = BuffWin32  ;
182             WCHAR BuffNT[0xffff /* Maximum USHORT size */];
183             UNICODE_STRING NTCmdLine;
184 
185             DWORD dwSize, dwStringSize;
186 
187             /*
188              * Format of the data file :
189              *
190              * [size_of_string 4 bytes][null_terminated_C_string]
191              * [size_of_string 4 bytes][null_terminated_C_string]
192              * [UNICODE_STRING_structure][string_buffer_of_UNICODE_STRING]
193              */
194 
195             /* 1- Read the WinMain's command line. */
196             dwStringSize = 0;
197 
198             ReadFile(hFile,
199                      &dwStringSize,
200                      sizeof(dwStringSize),
201                      &dwSize,
202                      NULL);
203 
204             dwStringSize = min(dwStringSize, sizeof(BuffWinMain));
205             ReadFile(hFile,
206                      WinMainCmdLine,
207                      dwStringSize,
208                      &dwSize,
209                      NULL);
210             *(LPWSTR)((ULONG_PTR)WinMainCmdLine + dwStringSize) = 0;
211 
212             /* 2- Read the Win32 mode command line. */
213             dwStringSize = 0;
214 
215             ReadFile(hFile,
216                      &dwStringSize,
217                      sizeof(dwStringSize),
218                      &dwSize,
219                      NULL);
220 
221             dwStringSize = min(dwStringSize, sizeof(BuffWin32));
222             ReadFile(hFile,
223                      Win32CmdLine,
224                      dwStringSize,
225                      &dwSize,
226                      NULL);
227             *(LPWSTR)((ULONG_PTR)Win32CmdLine + dwStringSize) = 0;
228 
229             /* 3- Finally, read the UNICODE_STRING command line. */
230             ReadFile(hFile,
231                      &NTCmdLine,
232                      sizeof(NTCmdLine),
233                      &dwSize,
234                      NULL);
235 
236             NTCmdLine.Buffer = BuffNT;
237             ReadFile(hFile,
238                      NTCmdLine.Buffer,
239                      NTCmdLine.Length,
240                      &dwSize,
241                      NULL);
242 
243             /* Now close the file. */
244             CloseHandle(hFile);
245 
246             /*
247              * Remove the program's name in the Win32 and NT command lines.
248              */
249             Win32CmdLine = ExtractCmdLine(Win32CmdLine);
250             ExtractCmdLine_U(&NTCmdLine);
251 
252             /* Print the results */
253             /*
254             *(LPWSTR)((ULONG_PTR)NTCmdLine.Buffer + NTCmdLine.Length) = 0;
255             printf("WinMain cmdline = '%S'\n"
256                    "Win32   cmdline = '%S'\n"
257                    "NT      cmdline = '%S'\n"
258                    "NT       length = %u\n",
259                    WinMainCmdLine,
260                    Win32CmdLine,
261                    NTCmdLine.Buffer, NTCmdLine.Length);
262             */
263 
264             /*
265              * Now check the results.
266              */
267             dwStringSize = min(lstrlenW(WinMainCmdLine), lstrlenW(Win32CmdLine));
268             ok(wcslen(WinMainCmdLine) == wcslen(Win32CmdLine), "Test %lu - WinMain and Win32 command lines do not have the same length !\n", TestNumber);
269             ok(wcsncmp(WinMainCmdLine, Win32CmdLine, dwStringSize) == 0, "Test %lu - WinMain and Win32 command lines are different !\n", TestNumber);
270 
271             dwStringSize = min(lstrlenW(WinMainCmdLine), NTCmdLine.Length / sizeof(WCHAR));
272             ok(wcsncmp(WinMainCmdLine, NTCmdLine.Buffer, dwStringSize) == 0, "Test %lu - WinMain and NT command lines are different !\n", TestNumber);
273 
274             dwStringSize = min(lstrlenW(Win32CmdLine), NTCmdLine.Length / sizeof(WCHAR));
275             ok(wcsncmp(Win32CmdLine, NTCmdLine.Buffer, dwStringSize) == 0, "Test %lu - Win32 and NT command lines are different !\n", TestNumber);
276         }
277     }
278 
279     /*
280      * Always delete the data file.
281      */
282     DeleteFileW(DATAFILE);
283 }
284 
285 START_TEST(CommandLine)
286 {
287     ULONG i;
288 
289     DWORD dwRet;
290     LPWSTR p = NULL;
291 
292 
293     /*
294      * Initialize the UtilityProgramDirectory variable.
295      */
296     dwRet = GetModuleFileNameW(NULL, UtilityProgramDirectory, COUNT_OF(UtilityProgramDirectory));
297     ok(dwRet != 0, "ERROR: Cannot retrieve the path to the current running process, last error %lu\n", GetLastError());
298     if (dwRet == 0) 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"testdata\\CmdLineUtil.exe");
308 
309     /* Close the opened quote if needed. */
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 */
323