[PATCH v2] find: First simple implementation and tests

Fabian Maurer dark.shadow4 at web.de
Sat Jul 21 14:42:21 CDT 2018


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 at 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();
+}
-- 
2.18.0




More information about the wine-devel mailing list