The message text is the same as on windows, since the exit code doesn't seem reliable.
v2: Read input text line by line v3: Use unicode v4: Use buffered reading
Signed-off-by: Fabian Maurer dark.shadow4@web.de --- programs/find/Makefile.in | 3 + programs/find/find.c | 171 ++++++++++++++++++++++- programs/find/resources.h | 27 ++++ programs/find/rsrc.rc | 25 ++++ programs/find/tests/Makefile.in | 5 + programs/find/tests/find.c | 231 ++++++++++++++++++++++++++++++++ 6 files changed, 458 insertions(+), 4 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/programs/find/Makefile.in b/programs/find/Makefile.in index ef8d61b7ce..edc23ec094 100644 --- a/programs/find/Makefile.in +++ b/programs/find/Makefile.in @@ -1,4 +1,7 @@ MODULE = find.exe APPMODE = -mconsole -municode +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..d181cf23df 100644 --- a/programs/find/find.c +++ b/programs/find/find.c @@ -16,18 +16,181 @@ * 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);
+BOOL read_char_from_handle(HANDLE handle, char *char_out) +{ + static char buffer[4096]; + static UINT buffer_max = 0; + static UINT buffer_pos = 0; + + /* Read next content into buffer */ + if (buffer_pos >= buffer_max) + { + BOOL success = ReadFile(handle, buffer, 4096, &buffer_max, NULL); + if (!success) + return FALSE; + buffer_pos = 0; + } + + *char_out = buffer[buffer_pos++]; + return TRUE; +} + +/* Reads a line from a handle, returns TRUE if there is more to be read */ +BOOL read_line_from_handle(HANDLE handle, WCHAR **line_out) +{ + int buffer_size = 4096; + char c; + int length = 0; + WCHAR *line_converted; + int line_converted_length; + BOOL success; + char *line = heap_alloc(buffer_size); + + for (;;) + { + success = read_char_from_handle(handle, &c); + + /* Check for EOF */ + if (!success) + { + if (length == 0) + return FALSE; + else + break; + } + + if (c == '\n') + break; + + /* Make sure buffer is large enough */ + if (length + 1 >= 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_converted_length = MultiByteToWideChar(CP_ACP, 0, line, -1, 0, 0); + line_converted = heap_alloc(line_converted_length * sizeof(WCHAR)); + MultiByteToWideChar(CP_ACP, 0, line, -1, line_converted, line_converted_length); + + heap_free(line); + + *line_out = line_converted; + return TRUE; +} + +void write_to_handle(HANDLE handle, const WCHAR *str) +{ + DWORD bytes_written_sum = 0; + char *str_converted; + UINT str_converted_length; + UINT str_length = lstrlenW(str); + + str_converted_length = WideCharToMultiByte(CP_ACP, 0, str, str_length, NULL, 0, NULL, NULL); + str_converted = heap_alloc(str_converted_length); + WideCharToMultiByte(CP_ACP, 0, str, str_length, str_converted, str_converted_length, NULL, NULL); + + do + { + DWORD bytes_written; + WriteFile(handle, str_converted, str_converted_length, &bytes_written, NULL); + bytes_written_sum += bytes_written; + } while (bytes_written_sum < str_converted_length); + + heap_free(str_converted); +} + +void find_printf(const WCHAR *str) +{ + write_to_handle(GetStdHandle(STD_OUTPUT_HANDLE), str); +} + +BOOL run_find_for_line(const WCHAR *line, const WCHAR *tofind) +{ + void *found; + WCHAR lineending[] = {'\r', '\n', 0}; + + if (lstrlenW(line) == 0) + return FALSE; + + found = StrStrW(line, tofind); + + if (found) + { + find_printf(line); + find_printf(lineending); + return TRUE; + } + + return FALSE; +} + int wmain(int argc, WCHAR *argv[]) { + WCHAR *line; + WCHAR *tofind = NULL; + WCHAR message_parameter_invalid[64]; + WCHAR 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", wine_dbgstr_w(argv[i])); + } + TRACE("\n"); + + LoadStringW(GetModuleHandleW(NULL), IDS_INVALID_PARAMETER, message_parameter_invalid, sizeof(message_parameter_invalid)); + LoadStringW(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..c5b359bd98 --- /dev/null +++ b/programs/find/tests/Makefile.in @@ -0,0 +1,5 @@ +TESTDLL = find.exe +IMPORTS = user32 + +C_SRCS = \ + find.c diff --git a/programs/find/tests/find.c b/programs/find/tests/find.c new file mode 100644 index 0000000000..fa10919c8d --- /dev/null +++ b/programs/find/tests/find.c @@ -0,0 +1,231 @@ +/* + * 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" + +WCHAR* read_all_from_handle(HANDLE handle) +{ + char buffer[4096]; + DWORD bytes_read; + DWORD length = 0; + BOOL success; + char *ret = heap_alloc_zero(1); + WCHAR *ret_converted; + int ret_converted_length; + + 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; + + ret_converted_length = MultiByteToWideChar(CP_UTF8, 0, ret, -1, 0, 0); + ret_converted = heap_alloc(ret_converted_length * sizeof(WCHAR)); + MultiByteToWideChar(CP_UTF8, 0, ret, -1, ret_converted, ret_converted_length); + + heap_free(ret); + + return ret_converted; +} + +/* Copied from find.exe implementation */ +void write_to_handle(HANDLE handle, const WCHAR *str) +{ + DWORD bytes_written_sum = 0; + char *str_converted; + UINT str_converted_length; + UINT str_length = lstrlenW(str); + + str_converted_length = WideCharToMultiByte(CP_UTF8, 0, str, str_length, NULL, 0, NULL, NULL); + str_converted = heap_alloc(str_converted_length); + WideCharToMultiByte(CP_UTF8, 0, str, str_length, str_converted, str_converted_length, NULL, NULL); + + do + { + DWORD bytes_written; + WriteFile(handle, str_converted, str_converted_length, &bytes_written, NULL); + bytes_written_sum += bytes_written; + } while (bytes_written_sum < str_converted_length); + + heap_free(str_converted); +} + +#define run_findW(commandline, input, out_expected, exitcode_expected) \ + run_findW_(commandline, input, out_expected, exitcode_expected, __FILE__, __LINE__) + +static void run_findW_(const WCHAR *commandline, const WCHAR *input, const WCHAR *out_expected, int exitcode_expected, const char *file, int line) +{ + static const WCHAR find_exe[] = {'f','i','n','d','.','e','x','e',' ','%','s'}; + HANDLE child_stdin_read; + HANDLE child_stdout_write; + HANDLE parent_stdin_write; + HANDLE parent_stdout_read; + STARTUPINFOW startup_info = {0}; + SECURITY_ATTRIBUTES security_attributes; + PROCESS_INFORMATION process_info = {0}; + WCHAR *child_output = NULL; + WCHAR 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; + + wsprintfW(cmd, find_exe, commandline); + CreateProcessW(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 = lstrcmpW(child_output, out_expected); + + ok_(file, line)(comparison == 0, "\n#################### Expected:\n" + "%s\n" + "#################### But got:\n" + "%s\n" + "####################\n", + wine_dbgstr_w(out_expected), wine_dbgstr_w(child_output)); + ok_(file, line)(exitcode == exitcode_expected, "Expected exitcode %d, got %d\n", exitcode_expected, exitcode); + + heap_free(child_output); +} + +#define run_findA(commandline, input, out_expected, exitcode_expected) \ + run_findA_(commandline, input, out_expected, exitcode_expected, __FILE__, __LINE__) + +static void run_findA_(const char *commandline, const char *input, const char *out_expected, int exitcode_expected, const char *file, int line) +{ + WCHAR *commandlineW; + WCHAR *inputW; + WCHAR *out_expectedW; + int len_commandlineW, len_inputW, len_out_expectedW; + + + len_commandlineW = MultiByteToWideChar(CP_UTF8, 0, commandline, -1, 0, 0); + commandlineW = heap_alloc(len_commandlineW * sizeof(WCHAR)); + MultiByteToWideChar(CP_UTF8, 0, commandline, -1, commandlineW, len_commandlineW); + + len_inputW = MultiByteToWideChar(CP_UTF8, 0, input, -1, 0, 0); + inputW = heap_alloc(len_inputW * sizeof(WCHAR)); + MultiByteToWideChar(CP_UTF8, 0, input, -1, inputW, len_inputW); + + len_out_expectedW = MultiByteToWideChar(CP_UTF8, 0, out_expected, -1, 0, 0); + out_expectedW = heap_alloc(len_out_expectedW * sizeof(WCHAR)); + MultiByteToWideChar(CP_UTF8, 0, out_expected, -1, out_expectedW, len_out_expectedW); + + run_findW_(commandlineW, inputW, out_expectedW, exitcode_expected, file, line); + + heap_free(commandlineW); + heap_free(inputW); + heap_free(out_expectedW); +} + +static void test_errors(void) +{ + run_findA("", "", "FIND: Parameter format not correct\r\n", 2); + todo_wine + run_findA("test", "", "FIND: Parameter format not correct\r\n", 2); + todo_wine + run_findA(""test", "", "FIND: Parameter format not correct\r\n", 2); + run_findA(""test" /XYZ", "", "FIND: Invalid switch\r\n", 2); +} + +static void test_singleline_without_switches(void) +{ + run_findA("""", "test", "", 1); + run_findA(""test"", "", "", 1); + run_findA(""test"", "test", "test\r\n", 0); + run_findA(""test"", "test2", "test2\r\n", 0); + run_findA(""test2"", "test", "", 1); + +} + +static void test_multiline(void) +{ + /* Newline in input shouldn't work */ + run_findA(""t1\r\nt1"", "t1\r\nt1", "", 1); + run_findA(""t1\nt1"", "t1\nt1", "", 1); + + /* Newline should always be displayed as \r\n */ + run_findA(""test1"", "test1\ntest2", "test1\r\n", 0); + run_findA(""test1"", "test1\r\ntest2", "test1\r\n", 0); + + /* Test with empty line */ + run_findA(""test1"", "test1\n\ntest2", "test1\r\n", 0); + + /* Two strings to be found */ + run_findA(""test"", "junk1\ntest1\ntest2\r\njunk", "test1\r\ntest2\r\n", 0); +} + +static void test_unicode_support(void) +{ + static const WCHAR str_empty[] = {0}; + static const WCHAR str_hello_in_russian[] = {'H', 'e', 'l', 'l', 'o', ' ', 0x043F, 0x0440, 0x0438, 0x0432, 0x0435, 0x0442, 0}; + static const WCHAR str_quoted_part_of_hello_in_russian[] = {'"', 0x0438, 0x0432, 0x0435, '"', 0}; + static const WCHAR str_hello_in_russian_with_endl[] = {'H', 'e', 'l', 'l', 'o', ' ', 0x043F, 0x0440, 0x0438, 0x0432, 0x0435, 0x0442, '\r', '\n', 0}; + static const WCHAR str_quoted_hello[] = {'"', 'H', 'e', 'l', 'l', 'o', '"', 0}; + + run_findW(str_quoted_hello, str_hello_in_russian, str_hello_in_russian_with_endl, 0); + run_findW(str_quoted_part_of_hello_in_russian, str_hello_in_russian, str_empty, 1); +} + +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(); + test_unicode_support(); +}
On 09/15/2018 06:42 PM, Fabian Maurer wrote:
The message text is the same as on windows, since the exit code doesn't seem reliable.
Is it unreliable across Windows versions? I only see error message for invalid input, not for the case when string was or wasn't found. So why does it matter?
+BOOL read_char_from_handle(HANDLE handle, char *char_out)
Function name does not correspond with what function is doing.
+{
- static char buffer[4096];
- static UINT buffer_max = 0;
- static UINT buffer_pos = 0;
What's a reason for this to be static?
- /* Read next content into buffer */
- if (buffer_pos >= buffer_max)
- {
BOOL success = ReadFile(handle, buffer, 4096, &buffer_max, NULL);
if (!success)
Separate variable does not seem to be necessary.
return FALSE;
buffer_pos = 0;
- }
- *char_out = buffer[buffer_pos++];
- return TRUE;
+}
+/* Reads a line from a handle, returns TRUE if there is more to be read */ +BOOL read_line_from_handle(HANDLE handle, WCHAR **line_out) +{
- int buffer_size = 4096;
- char c;
- int length = 0;
- WCHAR *line_converted;
- int line_converted_length;
- BOOL success;
- char *line = heap_alloc(buffer_size);
- for (;;)
- {
success = read_char_from_handle(handle, &c);
/* Check for EOF */
if (!success)
{
if (length == 0)
return FALSE;
else
break;
}
if (c == '\n')
break;
/* Make sure buffer is large enough */
if (length + 1 >= 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_converted_length = MultiByteToWideChar(CP_ACP, 0, line, -1, 0, 0);
- line_converted = heap_alloc(line_converted_length * sizeof(WCHAR));
- MultiByteToWideChar(CP_ACP, 0, line, -1, line_converted, line_converted_length);
- heap_free(line);
- *line_out = line_converted;
- return TRUE;
+}
CP_ACP is probably not correct in this case.
+void write_to_handle(HANDLE handle, const WCHAR *str) +{
- DWORD bytes_written_sum = 0;
- char *str_converted;
- UINT str_converted_length;
- UINT str_length = lstrlenW(str);
- str_converted_length = WideCharToMultiByte(CP_ACP, 0, str, str_length, NULL, 0, NULL, NULL);
- str_converted = heap_alloc(str_converted_length);
- WideCharToMultiByte(CP_ACP, 0, str, str_length, str_converted, str_converted_length, NULL, NULL);
- do
- {
DWORD bytes_written;
WriteFile(handle, str_converted, str_converted_length, &bytes_written, NULL);
bytes_written_sum += bytes_written;
- } while (bytes_written_sum < str_converted_length);
- heap_free(str_converted);
+}
+void find_printf(const WCHAR *str) +{
- write_to_handle(GetStdHandle(STD_OUTPUT_HANDLE), str);
+}
Check how it's done in reg/regedit, probably you should reuse it here. Less important, but why two helpers here? And why do you need a loop to write this buffer?
+BOOL run_find_for_line(const WCHAR *line, const WCHAR *tofind) +{
- void *found;
- WCHAR lineending[] = {'\r', '\n', 0};
- if (lstrlenW(line) == 0)
return FALSE;
- found = StrStrW(line, tofind);
- if (found)
- {
find_printf(line);
find_printf(lineending);
return TRUE;
- }
- return FALSE;
+}
There's strstrW(), importing shlwapi only for that is too much.
WCHAR message_parameter_invalid[64];
WCHAR message_switch_invalid[64]; int i;
LoadStringW(GetModuleHandleW(NULL), IDS_INVALID_PARAMETER, message_parameter_invalid, sizeof(message_parameter_invalid));
LoadStringW(GetModuleHandleW(NULL), IDS_INVALID_SWITCH, message_switch_invalid, sizeof(message_switch_invalid));
You don't need them both at the same time. Also check the arguments.
- 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);
- }
I don't understand why you're reading from stdin uncoditionally. This utility takes options, string to find, and a list of file paths. Only when paths are not provided it should read from stdin. Will this work if you have your string argument starts with '/'?
On Samstag, 15. September 2018 20:42:46 CEST Nikolay Sivov wrote:
On 09/15/2018 06:42 PM, Fabian Maurer wrote:
The message text is the same as on windows, since the exit code doesn't seem reliable.
Is it unreliable across Windows versions? I only see error message for invalid input, not for the case when string was or wasn't found. So why does it matter?
I might have mixed that up with fc.exe, I'll take a second look.
+BOOL read_char_from_handle(HANDLE handle, char *char_out)
Function name does not correspond with what function is doing.
You mean because it does buffering, too?
- static char buffer[4096];
- static UINT buffer_max = 0;
- static UINT buffer_pos = 0;
What's a reason for this to be static?
So it's not lost when the function returns. We read a lot from the handle, but only use one char at a time. This buffering is needed since reading one char at a time with ReadFile would be to inefficient.
- /* Read next content into buffer */
- if (buffer_pos >= buffer_max)
- {
BOOL success = ReadFile(handle, buffer, 4096, &buffer_max, NULL);
if (!success)
Separate variable does not seem to be necessary.
BOOL success you mean? I always pull that out of an if to make it more readable.
- line_converted_length = MultiByteToWideChar(CP_ACP, 0, line, -1, 0,
0); + line_converted = heap_alloc(line_converted_length * sizeof(WCHAR)); + MultiByteToWideChar(CP_ACP, 0, line, -1, line_converted, line_converted_length); +
- heap_free(line);
- *line_out = line_converted;
- return TRUE;
+}
CP_ACP is probably not correct in this case.
Maybe, but it works in the tests for now. What would you propose?
+void write_to_handle(HANDLE handle, const WCHAR *str) +{
- DWORD bytes_written_sum = 0;
- char *str_converted;
- UINT str_converted_length;
- UINT str_length = lstrlenW(str);
- str_converted_length = WideCharToMultiByte(CP_ACP, 0, str,
str_length, NULL, 0, NULL, NULL); + str_converted = heap_alloc(str_converted_length);
- WideCharToMultiByte(CP_ACP, 0, str, str_length, str_converted,
str_converted_length, NULL, NULL); +
- do
- {
DWORD bytes_written;
WriteFile(handle, str_converted, str_converted_length,
&bytes_written, NULL); + bytes_written_sum += bytes_written;
- } while (bytes_written_sum < str_converted_length);
- heap_free(str_converted);
+}
+void find_printf(const WCHAR *str) +{
- write_to_handle(GetStdHandle(STD_OUTPUT_HANDLE), str);
+}
Check how it's done in reg/regedit, probably you should reuse it here. Less important, but why two helpers here? And why do you need a loop to write this buffer?
I'll take a look at it. AFAIK WriteFile doesn't guarantee all bytes to be written at once, that's why we have the "bytes written" parameter. Correct me if I'm wrong.
There's strstrW(), importing shlwapi only for that is too much.
Ah, missed that one. I'll change it.
WCHAR message_parameter_invalid[64];
WCHAR message_switch_invalid[64];
int i;
LoadStringW(GetModuleHandleW(NULL), IDS_INVALID_PARAMETER,
message_parameter_invalid, sizeof(message_parameter_invalid)); + LoadStringW(GetModuleHandleW(NULL), IDS_INVALID_SWITCH, message_switch_invalid, sizeof(message_switch_invalid));
You don't need them both at the same time. Also check the arguments.
Sure thing, just loaded all of them at once for simplicity. Performance wise it shouldn't matter, but do you want me to load them only when needed?
By checking the arguments, do you mean I should call it with buffer length 0 to get a readonly pointer?
On 09/15/2018 11:38 PM, Fabian Maurer wrote:
- /* Read next content into buffer */
- if (buffer_pos >= buffer_max)
- {
- BOOL success = ReadFile(handle, buffer, 4096, &buffer_max, NULL);
- if (!success)
Separate variable does not seem to be necessary.
BOOL success you mean? I always pull that out of an if to make it more readable.
- line_converted_length = MultiByteToWideChar(CP_ACP, 0, line, -1, 0,
0); + line_converted = heap_alloc(line_converted_length *
sizeof(WCHAR)); + MultiByteToWideChar(CP_ACP, 0, line, -1,
line_converted, line_converted_length); +
- heap_free(line);
- *line_out = line_converted;
- return TRUE;
+}
CP_ACP is probably not correct in this case.
Maybe, but it works in the tests for now. What would you propose?
Again, take a look how output works in reg or regedit.
+void write_to_handle(HANDLE handle, const WCHAR *str)
+{
- DWORD bytes_written_sum = 0;
- char *str_converted;
- UINT str_converted_length;
- UINT str_length = lstrlenW(str);
- str_converted_length = WideCharToMultiByte(CP_ACP, 0, str,
str_length, NULL, 0, NULL, NULL); + str_converted =
heap_alloc(str_converted_length);
- WideCharToMultiByte(CP_ACP, 0, str, str_length, str_converted,
str_converted_length, NULL, NULL); +
- do
- {
- DWORD bytes_written;
- WriteFile(handle, str_converted, str_converted_length,
&bytes_written, NULL); + bytes_written_sum += bytes_written;
- } while (bytes_written_sum < str_converted_length);
- heap_free(str_converted);
+}
+void find_printf(const WCHAR *str)
+{
- write_to_handle(GetStdHandle(STD_OUTPUT_HANDLE), str);
+}
Check how it's done in reg/regedit, probably you should reuse it here.
Less important, but why two helpers here? And why do you need a loop to
write this buffer?
I'll take a look at it. AFAIK WriteFile doesn't guarantee all bytes to be written at once, that's why we have the "bytes written" parameter. Correct me if I'm wrong.
I think it's guaranteed for synchronous case.
There's strstrW(), importing shlwapi only for that is too much.
Ah, missed that one. I'll change it.
- WCHAR message_parameter_invalid[64];
- WCHAR message_switch_invalid[64];
int i;
- LoadStringW(GetModuleHandleW(NULL), IDS_INVALID_PARAMETER,
message_parameter_invalid, sizeof(message_parameter_invalid)); +
LoadStringW(GetModuleHandleW(NULL), IDS_INVALID_SWITCH,
message_switch_invalid, sizeof(message_switch_invalid));
You don't need them both at the same time. Also check the arguments.
Sure thing, just loaded all of them at once for simplicity. Performance wise it shouldn't matter, but do you want me to load them only when needed?
By checking the arguments, do you mean I should call it with buffer length 0 to get a readonly pointer?
I mean length is wrong.
- 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);
- }
I don't understand why you're reading from stdin uncoditionally. This
utility takes options, string to find, and a list of file paths. Only
when paths are not provided it should read from stdin. Will this work if
you have your string argument starts with '/'?
It's only supposed to be a first implementation, I don't want to add everything in one huge commit. It's already gotten too big for my likes, I don't think we should add even more functionality for the first version.
I'm not saying it should support everything from the start, but handling of command line arguments is important. It should print some warnings and/or abort if file paths are supplied, otherwise it will silently return nothing.
Regards,
Fabian Maurer
reg uses GetConsoleOutputCP, which would break the tests I wrote for find.
Which solution would you use?
Regards, Fabian Maurer
For some reason it cut off my mail...
I tried using ReadConsoleW to make things easier, but that doesn't work with redirected console handles. So I have to do it with ReadFile. I also planned to use that function to read from an handle for reading line-by-line from files, too.
reg uses GetConsoleOutputCP, which would break the tests I wrote for find - so I can't really use that either.
Question is, is mine still a bad approach, what'd you suggest?
Regards, Fabian Maurer