The message text is the same as on windows, since the exit code doesn't seem reliable.
v2: Read input text line by line
Signed-off-by: Fabian Maurer dark.shadow4@web.de --- configure | 25 ++--- configure.ac | 1 + programs/find/Makefile.in | 5 +- programs/find/find.c | 137 +++++++++++++++++++++++++- programs/find/resources.h | 27 +++++ programs/find/rsrc.rc | 25 +++++ programs/find/tests/Makefile.in | 4 + programs/find/tests/find.c | 169 ++++++++++++++++++++++++++++++++ 8 files changed, 369 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 337ce912fe..40f697d60c 100755 --- a/configure +++ b/configure @@ -805,7 +805,6 @@ infodir docdir oldincludedir includedir -runstatedir localstatedir sharedstatedir sysconfdir @@ -1870,7 +1869,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}' @@ -2123,15 +2121,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=* \ @@ -2269,7 +2258,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. @@ -2422,7 +2411,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] @@ -6803,7 +6791,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]; @@ -6849,7 +6837,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]; @@ -6873,7 +6861,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]; @@ -6918,7 +6906,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]; @@ -6942,7 +6930,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]; @@ -20030,6 +20018,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 be247ea200..e8edcbe522 100644 --- a/configure.ac +++ b/configure.ac @@ -3891,6 +3891,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..bbac3eeee0 100644 --- a/programs/find/find.c +++ b/programs/find/find.c @@ -16,18 +16,145 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include <windows.h> +#include <stdlib.h> +#include <shlwapi.h> + +#include "wine/heap.h" #include "wine/debug.h" +#include "resources.h"
WINE_DEFAULT_DEBUG_CHANNEL(find);
-int wmain(int argc, WCHAR *argv[]) +/* Reads a line from a handle, returns TRUE if there is more to be read */ +BOOL read_line_from_handle(HANDLE handle, char **line_out) +{ + int buffer_size = 4096; + char c; + DWORD bytes_read; + int length = 0; + BOOL success; + char *line = heap_alloc(buffer_size); + + for (;;) + { + success = ReadFile(handle, &c, 1, &bytes_read, NULL); + + if (c == '\n') + break; + + /* Check for EOF */ + if (!success || !bytes_read) + { + if (length == 0) + return FALSE; + else + break; + } + + /* Make sure buffer is large enough */ + if (length + bytes_read >= buffer_size) + { + buffer_size *= 2; + line = heap_realloc(line, buffer_size); + } + + line[length++] = c; + } + + line[length] = 0; + if (length - 1 >= 0 && line[length - 1] == '\r') + line[length - 1] = 0; + + *line_out = line; + return TRUE; +} + +void write_to_handle(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_handle(GetStdHandle(STD_OUTPUT_HANDLE), str); +} + +BOOL run_find_for_line(const char *line, const char *tofind) { + void *found; + + if (lstrlenA(line) == 0) + return FALSE; + + found = StrStrA(line, tofind); + + if (found) + { + find_printf(line); + find_printf("\r\n"); + return TRUE; + } + + return FALSE; +} + +int main(int argc, char *argv[]) +{ + char *line; + char *tofind = NULL; + char message_parameter_invalid[64]; + char message_switch_invalid[64]; int i; + int exitcode; + HANDLE input;
- 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)); + + input = GetStdHandle(STD_INPUT_HANDLE); + + for (i = 1; i < argc; i++) + { + if (argv[i][0] == '/') + { + find_printf(message_switch_invalid); + return 2; + } + else if(tofind == NULL) + { + tofind = argv[i]; + } + } + + if (tofind == NULL) + { + find_printf(message_parameter_invalid); + return 2; + } + + exitcode = 1; + while (read_line_from_handle(input, &line)) + { + if (run_find_for_line(line, tofind)) + exitcode = 0; + + heap_free(line); + }
- return 0; + 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..615d99ca59 --- /dev/null +++ b/programs/find/tests/find.c @@ -0,0 +1,169 @@ +/* + * 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 <windows.h> +#include <stdio.h> + +#include "wine/heap.h" +#include "wine/test.h" + +char* read_all_from_handle(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_handle(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_handle(parent_stdin_write, input); + CloseHandle(parent_stdin_write); + + child_output = read_all_from_handle(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); + +} + +static void test_multiline(void) +{ + /* Newline in input shouldn't work */ + run_find(""t1\r\nt1"", "t1\r\nt1", "", 1); + run_find(""t1\nt1"", "t1\nt1", "", 1); + + /* Newline should always be displayed as \r\n */ + run_find(""test1"", "test1\ntest2", "test1\r\n", 0); + run_find(""test1"", "test1\r\ntest2", "test1\r\n", 0); + + /* Test with empty line */ + run_find(""test1"", "test1\n\ntest2", "test1\r\n", 0); + + /* Two strings to be found */ + run_find(""test"", "junk1\ntest1\ntest2\r\njunk", "test1\r\ntest2\r\n", 0); +} + +START_TEST(find) +{ + if (PRIMARYLANGID(GetUserDefaultUILanguage()) != LANG_ENGLISH) + { + skip("Tests only work with english locale.\n"); + return; + } + + test_errors(); + test_singleline_without_switches(); + test_multiline(); +}