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(); +}
Fabian Maurer dark.shadow4@web.de wrote:
diff --git a/configure b/configure index 337ce912fe..40f697d60c 100755 --- a/configure +++ b/configure
Please avoid sending generated code, this unnecessarily bloats up size of the patch, and also changes not related random lines.
--- a/programs/find/Makefile.in +++ b/programs/find/Makefile.in @@ -1,4 +1,7 @@ MODULE = find.exe -APPMODE = -mconsole -municode +APPMODE = -mconsole
...
-int wmain(int argc, WCHAR *argv[])
...
+int main(int argc, char *argv[])
Please keep the application unicode, otherwise it won't work for utf-8 input and not proper ANSI code page (like en_US.UTF-8 locale and cyrillic string to find).
Please avoid sending generated code, this unnecessarily bloats up size of the patch, and also changes not related random lines.
Sorry, thought I should include configure.
Please keep the application unicode, otherwise it won't work for utf-8 input and not proper ANSI code page (like en_US.UTF-8 locale and cyrillic string to find).
Just tested cyrillic, it works in wine - but on windows it doesn't work. Or maybe I'm testing it wrong... Also, windows find.exe doesn't like unicode at all, utf-8 works though. Not sure how to deal with that, suggestions?
Regards, Fabian Maurer
Fabian Maurer dark.shadow4@web.de wrote:
Please keep the application unicode, otherwise it won't work for utf-8 input and not proper ANSI code page (like en_US.UTF-8 locale and cyrillic string to find).
Just tested cyrillic, it works in wine - but on windows it doesn't work. Or maybe I'm testing it wrong... Also, windows find.exe doesn't like unicode at all, utf-8 works though. Not sure how to deal with that, suggestions?
If you keep wmain() everything should start working automatically, there is no need for any special efforts.
If you keep wmain() everything should start working
automatically, there is
no need for any special efforts.
With wmain, don't I need to feed it WCHAR as parameters? Because that's exactly what doesn't work on windows. At least not the way I currently handle it with the pipes.
Regards, Fabian Maurer
Fabian Maurer dark.shadow4@web.de wrote:
If you keep wmain() everything should start working
automatically, there is
no need for any special efforts.
With wmain, don't I need to feed it WCHAR as parameters?
No, the conversion is done by somebody else (the start up or C run-time code).
No, the conversion is done by somebody else (the start up or C run-time code).
If I use wmain, then I read complete garbage from STD_INPUT_HANDLE - because that's not WCHARs, that's normal chars. And when I have to use MultiByteToWideChar there, why use wmain in the first place?
Fabian Maurer
Fabian Maurer dark.shadow4@web.de wrote:
No, the conversion is done by somebody else (the start up or C run-time code).
If I use wmain, then I read complete garbage from STD_INPUT_HANDLE - because that's not WCHARs, that's normal chars. And when I have to use MultiByteToWideChar there, why use wmain in the first place?
I'm talking about command line, not stdinput.
I'm talking about command line, not stdinput.
I know, but since you can't compare WCHAR with char, there has to be a conversion at some place. Either from the WCHAR commandline arguments to char, or from the char stdinput to WCHAR. Which would you chose, and why? Isn't information lost anyways?
Regards, Fabian Maurer
Fabian Maurer dark.shadow4@web.de wrote:
I'm talking about command line, not stdinput.
I know, but since you can't compare WCHAR with char, there has to be a conversion at some place. Either from the WCHAR commandline arguments to char, or from the char stdinput to WCHAR. Which would you chose, and why? Isn't information lost anyways?
Sometimes the loss is unavoidable, but if you have unicode and ansi input it's always better to perform further handling in unicode to avoid even more unwanted damage IMO.