[PATCH] find: First simple implementation and tests

Fabian Maurer dark.shadow4 at web.de
Sun Jun 10 12:32:09 CDT 2018


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 <dark.shadow4 at web.de>
---
 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 <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[])
+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 <windows.h>
+#include <stdio.h>
+
+#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




More information about the wine-devel mailing list