find.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. /*
  2. * Copyright 2019 Fabian Maurer
  3. *
  4. * This library is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU Lesser General Public
  6. * License as published by the Free Software Foundation; either
  7. * version 2.1 of the License, or (at your option) any later version.
  8. *
  9. * This library is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * Lesser General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Lesser General Public
  15. * License along with this library; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  17. */
  18. #include <windows.h>
  19. #include <stdio.h>
  20. #include "wine/heap.h"
  21. #include "wine/test.h"
  22. static void read_all_from_handle(HANDLE handle, BYTE **str, int *len)
  23. {
  24. char buffer[4096];
  25. DWORD bytes_read;
  26. DWORD length = 0;
  27. BYTE *ret = heap_alloc_zero(1);
  28. for (;;)
  29. {
  30. BOOL success = ReadFile(handle, buffer, sizeof(buffer), &bytes_read, NULL);
  31. if (!success || !bytes_read)
  32. break;
  33. ret = heap_realloc(ret, length + bytes_read);
  34. memcpy((char *)ret + length, buffer, bytes_read);
  35. length += bytes_read;
  36. }
  37. *str = ret;
  38. *len = length;
  39. }
  40. static void write_to_handle(HANDLE handle, const BYTE *str, int len)
  41. {
  42. DWORD dummy;
  43. WriteFile(handle, str, len, &dummy, NULL);
  44. }
  45. static void check_find_output(const BYTE *child_output, int child_output_len, const BYTE *out_expected, int out_expected_len, const char *file, int line)
  46. {
  47. BOOL strings_are_equal;
  48. char *child_output_copy;
  49. char *out_expected_copy;
  50. int i, pos;
  51. if (child_output_len != out_expected_len)
  52. strings_are_equal = FALSE;
  53. else
  54. {
  55. strings_are_equal = memcmp(child_output, out_expected, out_expected_len) == 0;
  56. }
  57. /* Format strings for debug printing */
  58. child_output_copy = heap_alloc_zero(child_output_len * 4 + 1);
  59. out_expected_copy = heap_alloc_zero(out_expected_len * 4 + 1);
  60. for (i = 0, pos = 0; i < child_output_len; i++)
  61. {
  62. if (child_output[i] && child_output[i] != '\r' && child_output[i] < 128)
  63. child_output_copy[pos++] = child_output[i];
  64. else
  65. {
  66. sprintf(&child_output_copy[pos], "\\x%02x", child_output[i]);
  67. pos += 4;
  68. }
  69. }
  70. for (i = 0, pos = 0; i < out_expected_len; i++)
  71. {
  72. if (out_expected[i] && out_expected[i] != '\r' && out_expected[i] < 128)
  73. out_expected_copy[pos++] = out_expected[i];
  74. else
  75. {
  76. sprintf(&out_expected_copy[pos], "\\x%02x", out_expected[i]);
  77. pos += 4;
  78. }
  79. }
  80. ok_(file, line)(strings_are_equal, "\n#################### Expected:\n"
  81. "%s\n"
  82. "#################### But got:\n"
  83. "%s\n"
  84. "####################\n",
  85. out_expected_copy, child_output_copy);
  86. heap_free(child_output_copy);
  87. heap_free(out_expected_copy);
  88. }
  89. static void mangle_text(const BYTE *input, int input_len, BYTE *output, int output_max, int *output_len) {
  90. WCHAR buffer[200];
  91. int count_wchar;
  92. /* Check for UTF-16 LE BOM */
  93. if (input[0] == 0xFF && input[1] == 0xFE)
  94. {
  95. int buffer_count = 0;
  96. int i;
  97. /* Copy utf16le into a WCHAR array, stripping the BOM */
  98. for (i = 2; i < input_len; i += 2)
  99. {
  100. buffer[buffer_count++] = input[i] + (input[i + 1] << 8);
  101. }
  102. *output_len = WideCharToMultiByte(GetConsoleCP(), 0, buffer, buffer_count, (char *)output, output_max, NULL, NULL);
  103. }
  104. else
  105. {
  106. count_wchar = MultiByteToWideChar(GetConsoleCP(), 0, (char *)input, input_len, buffer, ARRAY_SIZE(buffer));
  107. *output_len = WideCharToMultiByte(GetConsoleCP(), 0, buffer, count_wchar, (char *)output, output_max, NULL, NULL);
  108. }
  109. }
  110. static void run_find_stdin_(const WCHAR *commandline, const BYTE *input, int input_len, const BYTE *out_expected, int out_expected_len, int exitcode_expected, const char *file, int line)
  111. {
  112. HANDLE child_stdin_read;
  113. HANDLE child_stdout_write;
  114. HANDLE parent_stdin_write;
  115. HANDLE parent_stdout_read;
  116. STARTUPINFOW startup_info = {0};
  117. SECURITY_ATTRIBUTES security_attributes;
  118. PROCESS_INFORMATION process_info = {0};
  119. BYTE *child_output = NULL;
  120. int child_output_len;
  121. WCHAR cmd[4096];
  122. DWORD exitcode;
  123. security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
  124. security_attributes.bInheritHandle = TRUE;
  125. security_attributes.lpSecurityDescriptor = NULL;
  126. CreatePipe(&parent_stdout_read, &child_stdout_write, &security_attributes, 0);
  127. CreatePipe(&child_stdin_read, &parent_stdin_write, &security_attributes, 0);
  128. SetHandleInformation(parent_stdout_read, HANDLE_FLAG_INHERIT, 0);
  129. SetHandleInformation(parent_stdin_write, HANDLE_FLAG_INHERIT, 0);
  130. startup_info.cb = sizeof(STARTUPINFOW);
  131. startup_info.hStdInput = child_stdin_read;
  132. startup_info.hStdOutput = child_stdout_write;
  133. startup_info.hStdError = NULL;
  134. startup_info.dwFlags |= STARTF_USESTDHANDLES;
  135. wsprintfW(cmd, L"find.exe %s", commandline);
  136. CreateProcessW(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &startup_info, &process_info);
  137. CloseHandle(child_stdin_read);
  138. CloseHandle(child_stdout_write);
  139. write_to_handle(parent_stdin_write, input, input_len);
  140. CloseHandle(parent_stdin_write);
  141. read_all_from_handle(parent_stdout_read, &child_output, &child_output_len);
  142. CloseHandle(parent_stdout_read);
  143. GetExitCodeProcess(process_info.hProcess, &exitcode);
  144. CloseHandle(process_info.hProcess);
  145. CloseHandle(process_info.hThread);
  146. check_find_output(child_output, child_output_len, out_expected, out_expected_len, file, line);
  147. ok_(file, line)(exitcode == exitcode_expected, "Expected exitcode %d, got %ld\n", exitcode_expected, exitcode);
  148. heap_free(child_output);
  149. }
  150. static void run_find_file_(const WCHAR *commandline, const BYTE *input, int input_len, const BYTE *out_expected, int out_expected_len, int exitcode_expected, const char *file, int line)
  151. {
  152. char path_temp_file[MAX_PATH];
  153. char path_temp_dir[MAX_PATH];
  154. HANDLE handle_file;
  155. WCHAR commandline_new[MAX_PATH];
  156. BYTE *out_expected_new;
  157. char header[MAX_PATH];
  158. int header_len;
  159. GetTempPathA(ARRAY_SIZE(path_temp_dir), path_temp_dir);
  160. GetTempFileNameA(path_temp_dir, "", 0, path_temp_file);
  161. handle_file = CreateFileA(path_temp_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
  162. write_to_handle(handle_file, input, input_len);
  163. CloseHandle(handle_file);
  164. wsprintfW(commandline_new, L"%s %hs", commandline, path_temp_file);
  165. CharUpperA(path_temp_file);
  166. wsprintfA(header, "\r\n---------- %s\r\n", path_temp_file);
  167. header_len = lstrlenA(header);
  168. out_expected_new = heap_alloc(header_len + out_expected_len);
  169. memcpy(out_expected_new, header, header_len);
  170. memcpy(out_expected_new + header_len, out_expected, out_expected_len);
  171. run_find_stdin_(commandline_new, (BYTE*)"", 0, out_expected_new, header_len + out_expected_len, exitcode_expected, file, line);
  172. heap_free(out_expected_new);
  173. DeleteFileA(path_temp_file);
  174. }
  175. #define run_find_stdin_str(commandline, input, out_expected, exitcode_expected) \
  176. run_find_str_(commandline, input, out_expected, exitcode_expected, 0, __FILE__, __LINE__)
  177. #define run_find_file_str(commandline, input, out_expected, exitcode_expected) \
  178. run_find_str_(commandline, input, out_expected, exitcode_expected, 1, __FILE__, __LINE__)
  179. static void run_find_str_(const char *commandline, const char *input, const char *out_expected, int exitcode_expected, BOOL is_file, const char *file, int line)
  180. {
  181. WCHAR *commandlineW;
  182. int len_commandlineW;
  183. /* Turn commandline into WCHAR string */
  184. len_commandlineW = MultiByteToWideChar(CP_UTF8, 0, commandline, -1, 0, 0);
  185. commandlineW = heap_alloc(len_commandlineW * sizeof(WCHAR));
  186. MultiByteToWideChar(CP_UTF8, 0, commandline, -1, commandlineW, len_commandlineW);
  187. if (is_file)
  188. run_find_file_(commandlineW, (BYTE *)input, lstrlenA(input), (BYTE *)out_expected, lstrlenA(out_expected), exitcode_expected, file, line);
  189. else
  190. run_find_stdin_(commandlineW, (BYTE *)input, lstrlenA(input), (BYTE *)out_expected, lstrlenA(out_expected), exitcode_expected, file, line);
  191. heap_free(commandlineW);
  192. }
  193. #define run_find_stdin_unicode(input, out_expected, exitcode_expected) \
  194. run_find_unicode_(input, sizeof(input), out_expected, sizeof(out_expected), exitcode_expected, 0, __FILE__, __LINE__)
  195. #define run_find_file_unicode(input, out_expected, exitcode_expected) \
  196. run_find_unicode_(input, sizeof(input), out_expected, sizeof(out_expected), exitcode_expected, 1, __FILE__, __LINE__)
  197. static void run_find_unicode_(const BYTE *input, int input_len, const BYTE *out_expected, int out_expected_len, int exitcode_expected, BOOL is_file, const char *file, int line)
  198. {
  199. /* Need "test" as char and quoted wchar */
  200. static const WCHAR wstr_quoted_test[] = L"\"test\"";
  201. static const char str_test[] = "test";
  202. BYTE out_expected_mangled[200] = {0};
  203. int out_expected_mangled_len;
  204. mangle_text(out_expected, out_expected_len, out_expected_mangled, ARRAY_SIZE(out_expected_mangled), &out_expected_mangled_len);
  205. /* Mangling can destroy the test string, so check manually if it matches */
  206. if (!strstr((char*)out_expected_mangled, str_test))
  207. {
  208. out_expected_mangled_len = 0;
  209. exitcode_expected = 1;
  210. }
  211. if (is_file)
  212. run_find_file_(wstr_quoted_test, input, input_len, out_expected_mangled, out_expected_mangled_len, exitcode_expected, file, line);
  213. else
  214. run_find_stdin_(wstr_quoted_test, input, input_len, out_expected_mangled, out_expected_mangled_len, exitcode_expected, file, line);
  215. }
  216. static void run_find_file_multi(void)
  217. {
  218. char path_temp_file1[MAX_PATH];
  219. char path_temp_file2[MAX_PATH];
  220. char path_temp_file3[MAX_PATH];
  221. char path_temp_dir[MAX_PATH];
  222. HANDLE handle_file;
  223. WCHAR commandline_new[MAX_PATH];
  224. char out_expected[500];
  225. const char* input = "ab\nbd";
  226. GetTempPathA(ARRAY_SIZE(path_temp_dir), path_temp_dir);
  227. GetTempFileNameA(path_temp_dir, "", 0, path_temp_file1);
  228. GetTempFileNameA(path_temp_dir, "", 0, path_temp_file2);
  229. GetTempFileNameA(path_temp_dir, "", 0, path_temp_file3);
  230. handle_file = CreateFileA(path_temp_file1, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
  231. write_to_handle(handle_file, (BYTE*)input, strlen(input));
  232. CloseHandle(handle_file);
  233. handle_file = CreateFileA(path_temp_file2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
  234. write_to_handle(handle_file, (BYTE*)input, strlen(input));
  235. CloseHandle(handle_file);
  236. wsprintfW(commandline_new, L"\"b\" C:\\doesnotexist1 %hs C:\\doesnotexist1 %hs C:\\doesnotexist1 %hs", path_temp_file1, path_temp_file2, path_temp_file3);
  237. /* Keep file open during the test */
  238. handle_file = CreateFileA(path_temp_file3, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
  239. CharUpperA(path_temp_file1);
  240. CharUpperA(path_temp_file2);
  241. CharUpperA(path_temp_file3);
  242. wsprintfA(out_expected,
  243. "File not found - C:\\DOESNOTEXIST1\r\n"
  244. "\r\n---------- %s\r\n"
  245. "ab\r\nbd\r\n"
  246. "File not found - C:\\DOESNOTEXIST1\r\n"
  247. "\r\n---------- %s\r\n"
  248. "ab\r\nbd\r\n"
  249. "File not found - C:\\DOESNOTEXIST1\r\n"
  250. "File not found - %s\r\n",
  251. path_temp_file1, path_temp_file2, path_temp_file3);
  252. run_find_stdin_(commandline_new, (BYTE*)"", 0, (BYTE*)out_expected, strlen(out_expected), 0, __FILE__, __LINE__);
  253. CloseHandle(handle_file);
  254. DeleteFileA(path_temp_file1);
  255. DeleteFileA(path_temp_file2);
  256. DeleteFileA(path_temp_file3);
  257. }
  258. static void test_errors(void)
  259. {
  260. run_find_stdin_str("", "", "FIND: Parameter format not correct\r\n", 2);
  261. todo_wine /* Quotes are not properly passed into wine yet */
  262. run_find_stdin_str("test", "", "FIND: Parameter format not correct\r\n", 2);
  263. todo_wine /* Quotes are not properly passed into wine yet */
  264. run_find_stdin_str("\"test", "", "FIND: Parameter format not correct\r\n", 2);
  265. run_find_stdin_str("\"test\" /XYZ", "", "FIND: Invalid switch\r\n", 2);
  266. run_find_stdin_str("\"test\" C:\\doesnotexist.dat", "", "File not found - C:\\DOESNOTEXIST.DAT\r\n", 1);
  267. }
  268. static void test_singleline_without_switches(void)
  269. {
  270. run_find_stdin_str("\"\"", "test", "", 1);
  271. run_find_stdin_str("\"test\"", "", "", 1);
  272. run_find_stdin_str("\"test\"", "test", "test\r\n", 0);
  273. run_find_stdin_str("\"test\"", "test2", "test2\r\n", 0);
  274. run_find_stdin_str("\"test\"", "test\r2", "test\r2\r\n", 0);
  275. run_find_stdin_str("\"test2\"", "test", "", 1);
  276. }
  277. static void test_multiline(void)
  278. {
  279. /* Newline in input shouldn't work */
  280. run_find_stdin_str("\"t1\r\nt1\"", "t1\r\nt1", "", 1);
  281. run_find_stdin_str("\"t1\nt1\"", "t1\nt1", "", 1);
  282. /* Newline should always be displayed as \r\n */
  283. run_find_stdin_str("\"test1\"", "test1\ntest2", "test1\r\n", 0);
  284. run_find_stdin_str("\"test1\"", "test1\r\ntest2", "test1\r\n", 0);
  285. /* Test with empty line */
  286. run_find_stdin_str("\"test1\"", "test1\n\ntest2", "test1\r\n", 0);
  287. /* Two strings to be found */
  288. run_find_stdin_str("\"test\"", "junk1\ntest1\ntest2\r\njunk", "test1\r\ntest2\r\n", 0);
  289. }
  290. static const BYTE str_empty[] = {};
  291. static const BYTE str_jap_shiftjis[] = { 0x8E,0x84,0x82,0xCD,'t','e','s','t','!','\r','\n' };
  292. static const BYTE str_jap_utf8_bom[] = { 0xEF,0xBB,0xBF,0xE7,0xA7,0x81,0xE3,0x81,0xAF,'j','a','p','t','e','s','t','!','\r','\n' };
  293. static const BYTE str_jap_utf8_nobom[] = { 0xE7,0xA7,0x81,0xE3,0x81,0xAF,'j','a','p','t','e','s','t','!','\r','\n' };
  294. static const BYTE str_jap_utf16le_bom[] = { 0xFF,0xFE,0xC1,0x79,0x6F,0x30,'t',0,'e',0,'s',0,'t',0,'!',0,'\r',0,'\n',0 };
  295. static const BYTE str_jap_utf16le_nobom[] = { 0xC1,0x79,0x6F,0x30,'t',0,'e',0,'s',0,'t',0,'!',0 };
  296. static const BYTE str_jap_utf16be_bom[] = { 0xFE,0xFF,0x79,0xC1,0x30,0x6F,0,'t',0,'e',0,'s',0,'t',0,'!' };
  297. static const BYTE str_jap_utf16be_nobom[] = { 0x79,0xC1,0x30,0x6F,0,'t',0,'e',0,'s',0,'t',0,'!' };
  298. static const BYTE str_rus_utf8_bom[] = { 0xEF,0xBB,0xBF,0xD0,0xBF,0xD1,0x80,0xD0,0xB8,0xD0,0xB2,0xD0,0xB5,0xD1,0x82,0x20,'t','e','s','t','!','\r','\n' };
  299. static const BYTE str_rus_utf8_nobom[] = { 0xD0,0xBF,0xD1,0x80,0xD0,0xB8,0xD0,0xB2,0xD0,0xB5,0xD1,0x82,0x20,'t','e','s','t','!','\r','\n' };
  300. static const BYTE str_en_utf8_bom[] = { 0xEF,0xBB,0xBF,'e','n','t','e','s','t','\r','\n' };
  301. static const BYTE str_en_utf8_nobom[] = { 'e','n','t','e','s','t','\r','\n' };
  302. static void test_unicode_support_stdin(void)
  303. {
  304. /* Test unicode support on STDIN
  305. * Those depend on the active codepage - e.g. 932 (japanese) behaves different from 1252 (latin)
  306. * All unicode tests must check for the string "test".
  307. */
  308. /* Test UTF-8 BOM */
  309. run_find_stdin_unicode(str_en_utf8_nobom, str_en_utf8_nobom, 0);
  310. run_find_stdin_unicode(str_en_utf8_bom, str_en_utf8_bom, 0);
  311. /* Test russian characters */
  312. run_find_stdin_unicode(str_rus_utf8_bom, str_rus_utf8_bom, 0);
  313. run_find_stdin_unicode(str_rus_utf8_nobom, str_rus_utf8_nobom, 0);
  314. /* Test japanese characters */
  315. run_find_stdin_unicode(str_jap_utf8_nobom, str_jap_utf8_nobom, 0);
  316. run_find_stdin_unicode(str_jap_utf8_bom, str_jap_utf8_bom, 0);
  317. run_find_stdin_unicode(str_jap_shiftjis, str_jap_shiftjis, 0);
  318. /* Test unsupported encodings */
  319. run_find_stdin_unicode(str_jap_utf16le_nobom, str_empty, 1);
  320. run_find_stdin_unicode(str_jap_utf16be_bom, str_empty, 1);
  321. run_find_stdin_unicode(str_jap_utf16be_nobom, str_empty, 1);
  322. /* Test utf16le */
  323. todo_wine
  324. run_find_stdin_unicode(str_jap_utf16le_bom, str_jap_utf16le_bom, 0);
  325. }
  326. static void test_file_search(void)
  327. {
  328. run_find_file_str("\"\"", "test", "", 1);
  329. run_find_file_str("\"test\"", "", "", 1);
  330. run_find_file_str("\"test\"", "test", "test\r\n", 0);
  331. run_find_file_str("\"test\"", "test2", "test2\r\n", 0);
  332. run_find_file_str("\"test\"", "test\r2", "test\r2\r\n", 0);
  333. run_find_file_str("\"test2\"", "test", "", 1);
  334. run_find_file_str("\"test\"", "test\nother\ntest2\ntest3", "test\r\ntest2\r\ntest3\r\n", 0);
  335. }
  336. static void test_unicode_support_file(void)
  337. {
  338. /* Test unicode support on files */
  339. /* Test UTF-8 BOM */
  340. run_find_file_unicode(str_en_utf8_nobom, str_en_utf8_nobom, 0);
  341. run_find_file_unicode(str_en_utf8_bom, str_en_utf8_bom, 0);
  342. /* Test russian characters */
  343. run_find_file_unicode(str_rus_utf8_bom, str_rus_utf8_bom, 0);
  344. run_find_file_unicode(str_rus_utf8_nobom, str_rus_utf8_nobom, 0);
  345. /* Test japanese characters */
  346. run_find_file_unicode(str_jap_utf8_nobom, str_jap_utf8_nobom, 0);
  347. run_find_file_unicode(str_jap_utf8_bom, str_jap_utf8_bom, 0);
  348. run_find_file_unicode(str_jap_shiftjis, str_jap_shiftjis, 0);
  349. /* Test unsupported encodings */
  350. run_find_file_unicode(str_jap_utf16le_nobom, str_empty, 1);
  351. run_find_file_unicode(str_jap_utf16be_bom, str_empty, 1);
  352. run_find_file_unicode(str_jap_utf16be_nobom, str_empty, 1);
  353. /* Test utf16le */
  354. todo_wine
  355. run_find_file_unicode(str_jap_utf16le_bom, str_jap_utf16le_bom, 0);
  356. }
  357. START_TEST(find)
  358. {
  359. if (PRIMARYLANGID(GetUserDefaultUILanguage()) != LANG_ENGLISH)
  360. {
  361. skip("Error tests only work with english locale.\n");
  362. }
  363. else
  364. {
  365. test_errors();
  366. run_find_file_multi();
  367. }
  368. test_singleline_without_switches();
  369. test_multiline();
  370. test_unicode_support_stdin();
  371. test_file_search();
  372. test_unicode_support_file();
  373. }