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