From: Fabian Maurer Subject: [PATCH] find: First simple implementation and tests Message-Id: <20180610173209.28741-1-dark.shadow4@web.de> Date: Sun, 10 Jun 2018 19:32:09 +0200 The message text is the same as on windows, since the exit code doesn't seem reliable. Currently only supports single line searching, and not unicode aware. I'll add tests and if needed an implementation for that sometime later. Signed-off-by: Fabian Maurer --- configure | 25 ++---- configure.ac | 1 + programs/find/Makefile.in | 5 +- programs/find/find.c | 112 +++++++++++++++++++++-- programs/find/resources.h | 27 ++++++ programs/find/rsrc.rc | 25 ++++++ programs/find/tests/Makefile.in | 4 + programs/find/tests/find.c | 151 ++++++++++++++++++++++++++++++++ 8 files changed, 326 insertions(+), 24 deletions(-) create mode 100644 programs/find/resources.h create mode 100644 programs/find/rsrc.rc create mode 100644 programs/find/tests/Makefile.in create mode 100644 programs/find/tests/find.c diff --git a/configure b/configure index 299fb407d8..56e7dde04c 100755 --- a/configure +++ b/configure @@ -800,7 +800,6 @@ infodir docdir oldincludedir includedir -runstatedir localstatedir sharedstatedir sysconfdir @@ -1862,7 +1861,6 @@ datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' -runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' @@ -2115,15 +2113,6 @@ do | -silent | --silent | --silen | --sile | --sil) silent=yes ;; - -runstatedir | --runstatedir | --runstatedi | --runstated \ - | --runstate | --runstat | --runsta | --runst | --runs \ - | --run | --ru | --r) - ac_prev=runstatedir ;; - -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ - | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ - | --run=* | --ru=* | --r=*) - runstatedir=$ac_optarg ;; - -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ @@ -2261,7 +2250,7 @@ fi for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir runstatedir + libdir localedir mandir do eval ac_val=\$$ac_var # Remove trailing slashes. @@ -2414,7 +2403,6 @@ Fine tuning of the installation directories: --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] - --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] @@ -6626,7 +6614,7 @@ else We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -6672,7 +6660,7 @@ else We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -6696,7 +6684,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -6741,7 +6729,7 @@ else We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -6765,7 +6753,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -19379,6 +19367,7 @@ wine_fn_config_makefile programs/explorer enable_explorer wine_fn_config_makefile programs/extrac32 enable_extrac32 wine_fn_config_makefile programs/fc enable_fc wine_fn_config_makefile programs/find enable_find +wine_fn_config_makefile programs/find/tests enable_tests wine_fn_config_makefile programs/findstr enable_findstr wine_fn_config_makefile programs/fsutil enable_fsutil wine_fn_config_makefile programs/hh enable_hh diff --git a/configure.ac b/configure.ac index 469fa8d729..1d6a4cf1c6 100644 --- a/configure.ac +++ b/configure.ac @@ -3878,6 +3878,7 @@ WINE_CONFIG_MAKEFILE(programs/explorer) WINE_CONFIG_MAKEFILE(programs/extrac32) WINE_CONFIG_MAKEFILE(programs/fc) WINE_CONFIG_MAKEFILE(programs/find) +WINE_CONFIG_MAKEFILE(programs/find/tests) WINE_CONFIG_MAKEFILE(programs/findstr) WINE_CONFIG_MAKEFILE(programs/fsutil) WINE_CONFIG_MAKEFILE(programs/hh) diff --git a/programs/find/Makefile.in b/programs/find/Makefile.in index ef8d61b7ce..4a90905986 100644 --- a/programs/find/Makefile.in +++ b/programs/find/Makefile.in @@ -1,4 +1,7 @@ MODULE = find.exe -APPMODE = -mconsole -municode +APPMODE = -mconsole +IMPORTS = user32 shlwapi C_SRCS = find.c + +RC_SRCS = rsrc.rc diff --git a/programs/find/find.c b/programs/find/find.c index 9d7aecd402..937f773243 100644 --- a/programs/find/find.c +++ b/programs/find/find.c @@ -16,18 +16,120 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#include +#include +#include + +#include "wine/heap.h" #include "wine/debug.h" +#include "resources.h" WINE_DEFAULT_DEBUG_CHANNEL(find); -int wmain(int argc, WCHAR *argv[]) +char* read_from_pipe(HANDLE handle) +{ + char buffer[4096]; + DWORD bytes_read; + DWORD length = 0; + BOOL success; + char *ret = heap_alloc_zero(1); + + for (;;) + { + success = ReadFile(handle, buffer, sizeof(buffer), &bytes_read, NULL); + if (!success || !bytes_read) + break; + ret = heap_realloc(ret, length + bytes_read); + memcpy((char *)ret + length, buffer, bytes_read); + length += bytes_read; + } + + ret[length] = 0; + return ret; +} + +void write_to_pipe(HANDLE handle, const char *str) +{ + DWORD bytes_written_sum = 0; + DWORD length = lstrlenA(str); + do + { + DWORD bytes_written; + WriteFile(handle, str, length * sizeof(char), &bytes_written, NULL); + bytes_written_sum += bytes_written; + } while (bytes_written_sum < length); +} + +void find_printf(const char *str) +{ + write_to_pipe(GetStdHandle(STD_OUTPUT_HANDLE), str); +} + +int do_find(const char *text, const char* tofind) { + void *found; + + if (lstrlenA(text) == 0) + return 1; + + found = StrStrA(text, tofind); + + if (found) + { + find_printf(text); + find_printf("\r\n"); + return 0; + } + + return 1; +} + +int main(int argc, char *argv[]) +{ + char *text_stdin; + char *tofind = NULL; + char message_parameter_invalid[64]; + char message_switch_invalid[64]; int i; + int exitcode; - WINE_FIXME("stub:"); + TRACE("running find:"); for (i = 0; i < argc; i++) - WINE_FIXME(" %s", wine_dbgstr_w(argv[i])); - WINE_FIXME("\n"); + { + TRACE(" %s", argv[i]); + } + TRACE("\n"); + + LoadStringA(GetModuleHandleW(NULL), IDS_INVALID_PARAMETER, message_parameter_invalid, sizeof(message_parameter_invalid)); + LoadStringA(GetModuleHandleW(NULL), IDS_INVALID_SWITCH, message_switch_invalid, sizeof(message_switch_invalid)); + + /* We read the complete input at once, for simplicity */ + text_stdin = read_from_pipe(GetStdHandle(STD_INPUT_HANDLE)); + + for (i = 1; i < argc; i++) + { + if (argv[i][0] == '/') + { + find_printf(message_switch_invalid); + exitcode = 2; + goto cleanup; + } + else if(tofind == NULL) + { + tofind = argv[i]; + } + } + + if (tofind == NULL) + { + find_printf(message_parameter_invalid); + exitcode = 2; + goto cleanup; + } + + exitcode = do_find(text_stdin, tofind); - return 0; +cleanup: + heap_free(text_stdin); + return exitcode; } diff --git a/programs/find/resources.h b/programs/find/resources.h new file mode 100644 index 0000000000..8712d91e36 --- /dev/null +++ b/programs/find/resources.h @@ -0,0 +1,27 @@ +/* + * Resource IDs + * + * Copyright 2018 Fabian Maurer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_FIND_RESOURCES_H +#define __WINE_FIND_RESOURCES_H + +#define IDS_INVALID_PARAMETER 1000 +#define IDS_INVALID_SWITCH 1001 + +#endif /* __WINE_FIND_RESOURCES_H */ diff --git a/programs/find/rsrc.rc b/programs/find/rsrc.rc new file mode 100644 index 0000000000..f10fb6285f --- /dev/null +++ b/programs/find/rsrc.rc @@ -0,0 +1,25 @@ +/* + * Copyright 2018 Fabian Maurer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "resources.h" + +STRINGTABLE +{ + IDS_INVALID_PARAMETER "FIND: Parameter format not correct\r\n" + IDS_INVALID_SWITCH "FIND: Invalid switch\r\n" +} diff --git a/programs/find/tests/Makefile.in b/programs/find/tests/Makefile.in new file mode 100644 index 0000000000..ad88243ede --- /dev/null +++ b/programs/find/tests/Makefile.in @@ -0,0 +1,4 @@ +TESTDLL = find.exe + +C_SRCS = \ + find.c diff --git a/programs/find/tests/find.c b/programs/find/tests/find.c new file mode 100644 index 0000000000..2dce343965 --- /dev/null +++ b/programs/find/tests/find.c @@ -0,0 +1,151 @@ +/* + * Copyright 2018 Fabian Maurer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include + +#include "wine/heap.h" +#include "wine/test.h" + +/* Copied from find.exe implementation */ +char* read_from_pipe(HANDLE handle) +{ + char buffer[4096]; + DWORD bytes_read; + DWORD length = 0; + BOOL success; + char *ret = heap_alloc_zero(1); + + for (;;) + { + success = ReadFile(handle, buffer, sizeof(buffer), &bytes_read, NULL); + if (!success || !bytes_read) + break; + ret = heap_realloc(ret, length + bytes_read); + memcpy((char *)ret + length, buffer, bytes_read); + length += bytes_read; + } + + ret[length] = 0; + return ret; +} + +/* Copied from find.exe implementation */ +void write_to_pipe(HANDLE handle, const char *str) +{ + DWORD bytes_written_sum = 0; + DWORD length = lstrlenA(str); + do + { + DWORD bytes_written; + WriteFile(handle, str, length * sizeof(char), &bytes_written, NULL); + bytes_written_sum += bytes_written; + } while (bytes_written_sum < length); +} + +#define run_find(commandline, input, out_expected, exitcode_expected) \ + run_find_(commandline, input, out_expected, exitcode_expected, __FILE__, __LINE__) + +static void run_find_(const char *commandline, const char *input, const char *out_expected, int exitcode_expected, const char *file, int line) +{ + HANDLE child_stdin_read; + HANDLE child_stdout_write; + HANDLE parent_stdin_write; + HANDLE parent_stdout_read; + STARTUPINFOA startup_info = {0}; + SECURITY_ATTRIBUTES security_attributes; + PROCESS_INFORMATION process_info = {0}; + char *child_output = NULL; + char cmd[4096]; + int comparison; + DWORD exitcode; + + security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); + security_attributes.bInheritHandle = TRUE; + security_attributes.lpSecurityDescriptor = NULL; + + CreatePipe(&parent_stdout_read, &child_stdout_write, &security_attributes, 0); + CreatePipe(&child_stdin_read, &parent_stdin_write, &security_attributes, 0); + + SetHandleInformation(parent_stdout_read, HANDLE_FLAG_INHERIT, 0); + SetHandleInformation(parent_stdin_write, HANDLE_FLAG_INHERIT, 0); + + startup_info.cb = sizeof(STARTUPINFOW); + startup_info.hStdInput = child_stdin_read; + startup_info.hStdOutput = child_stdout_write; + startup_info.hStdError = NULL; + startup_info.dwFlags |= STARTF_USESTDHANDLES; + + sprintf(cmd, "find.exe %s", commandline); + CreateProcessA(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &startup_info, &process_info); + CloseHandle(child_stdin_read); + CloseHandle(child_stdout_write); + + write_to_pipe(parent_stdin_write, input); + CloseHandle(parent_stdin_write); + + child_output = read_from_pipe(parent_stdout_read); + CloseHandle(parent_stdout_read); + + GetExitCodeProcess(process_info.hProcess, &exitcode); + CloseHandle(process_info.hProcess); + CloseHandle(process_info.hThread); + + comparison = lstrcmpA(child_output, out_expected); + + ok_(file, line)(comparison == 0, "\n#################### Expected:\n" + "%s\n" + "#################### But got:\n" + "%s\n" + "####################\n", + out_expected, child_output); + ok_(file, line)(exitcode == exitcode_expected, "Expected exitcode %d, got %d\n", exitcode_expected, exitcode); + + heap_free(child_output); +} + +static void test_errors(void) +{ + run_find("", "", "FIND: Parameter format not correct\r\n", 2); + todo_wine + run_find("test", "", "FIND: Parameter format not correct\r\n", 2); + todo_wine + run_find("\"test", "", "FIND: Parameter format not correct\r\n", 2); + run_find("\"test\" /XYZ", "", "FIND: Invalid switch\r\n", 2); +} + +static void test_singleline_without_switches(void) +{ + run_find("\"\"", "test", "", 1); + run_find("\"test\"", "", "", 1); + run_find("\"test\"", "test", "test\r\n", 0); + run_find("\"test\"", "test2", "test2\r\n", 0); + run_find("\"test2\"", "test", "", 1); +} + +START_TEST(find) +{ + if (PRIMARYLANGID(GetUserDefaultUILanguage()) != LANG_ENGLISH) + { + skip("Tests only work with english locale.\n"); + return; + } + + test_errors(); + test_singleline_without_switches(); +} -- 2.17.1