This MR finishes the eradication of WCMD_ReadFile in favor of WCMD_fgets started in previous MR.
A couple of enhancements to MORE command.
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/tests/test_builtins.bat | 5 ++++- programs/cmd/tests/test_builtins.bat.exp | 4 ++-- programs/cmd/tests/test_builtins.cmd | 5 ++++- programs/cmd/tests/test_builtins.cmd.exp | 4 ++-- 4 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/programs/cmd/tests/test_builtins.bat b/programs/cmd/tests/test_builtins.bat index 05abc8de655..dc624111213 100644 --- a/programs/cmd/tests/test_builtins.bat +++ b/programs/cmd/tests/test_builtins.bat @@ -332,9 +332,12 @@ call :setError 666 & ((echo A | choice /C:BA) >NUL &&echo SUCCESS !errorlevel!|| call :setError 666 & (choice /C:BA <NUL >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) rem syntax errors in command return INVALID_FUNCTION, need to find a test for returning 255 echo --- success/failure for MORE command +echo a> filea call :setError 666 & (more NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) -call :setError 666 & (more I\dont\exist.txt > NUL 2>&1 &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) call :setError 666 & (echo foo | more &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +rem native 'MORE file' outputs to CONOUT$, not stdout! +call :setError 666 & (more filea I\dont\exist.txt &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +erase filea echo --- success/failure for PAUSE command call :setError 666 & (pause < NUL > NUL 2>&1 &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) rem TODO: pause is harder to test when fd 1 is a console handle as we don't control output diff --git a/programs/cmd/tests/test_builtins.bat.exp b/programs/cmd/tests/test_builtins.bat.exp index 608f3d11e15..d3f23abde77 100644 --- a/programs/cmd/tests/test_builtins.bat.exp +++ b/programs/cmd/tests/test_builtins.bat.exp @@ -225,10 +225,10 @@ FAILURE 2 FAILURE 1 --- success/failure for MORE command SUCCESS 0 -SUCCESS 0 foo@space@
SUCCESS 0 ---- success/failure for PAUSE command +@todo_wine@SUCCESS 0 +@todo_wine@--- success/failure for PAUSE command FAILURE 1 --- diff --git a/programs/cmd/tests/test_builtins.cmd b/programs/cmd/tests/test_builtins.cmd index 7ac989593df..19dc3c788bb 100644 --- a/programs/cmd/tests/test_builtins.cmd +++ b/programs/cmd/tests/test_builtins.cmd @@ -857,9 +857,12 @@ call :setError 666 & ((echo A | choice /C:BA) >NUL &&echo SUCCESS !errorlevel!|| call :setError 666 & (choice /C:BA <NUL >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) rem syntax errors in command return INVALID_FUNCTION, need to find a test for returning 255 echo --- success/failure for MORE command +echo a>filea call :setError 666 & (more NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) -call :setError 666 & (more I\dont\exist.txt > NUL 2>&1 &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) call :setError 666 & (echo foo | more &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +rem native 'MORE file' outputs to CONOUT$, not stdout! +call :setError 666 & (more filea I\dont\exist.txt &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +erase filea echo --- success/failure for PAUSE command call :setError 666 & (pause < NUL > NUL 2>&1 &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) rem TODO: pause is harder to test when fd 1 is a console handle as we don't control output diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp index dde889755aa..644ca910c8c 100644 --- a/programs/cmd/tests/test_builtins.cmd.exp +++ b/programs/cmd/tests/test_builtins.cmd.exp @@ -689,11 +689,11 @@ FAILURE 2 FAILURE 1 --- success/failure for MORE command SUCCESS 0 -SUCCESS 0 foo@space@
SUCCESS 0 ---- success/failure for PAUSE command +@todo_wine@SUCCESS 0 +@todo_wine@--- success/failure for PAUSE command FAILURE 1 --- --------- success/failure when invoking cmd /c --------------
From: Eric Pouech epouech@codeweavers.com
Clean-up: - using WCMD_fgets() to get input, - using helper to factorize the two forms of the MORE command.
Enhancements and let behavior be closer to native: - when using 'MORE file1 file2...', stop displaying files at the first non existing one, - can break MORE with ctrl-c, - the information display + wait at the end of each page is only activated when outputting to console, - the bottom information line is overwritten by next line.
Note native MORE outputs to CONOUT$ (not stdout), which makes it hard for adding tests.
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/builtins.c | 163 +++++++++++++++++++--------------------- programs/cmd/wcmd.h | 1 + programs/cmd/wcmdmain.c | 13 +++- 3 files changed, 88 insertions(+), 89 deletions(-)
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c index d777ac197ae..2db7d3d53aa 100644 --- a/programs/cmd/builtins.c +++ b/programs/cmd/builtins.c @@ -3462,112 +3462,101 @@ RETURN_CODE WCMD_type(WCHAR *args) return errorlevel = return_code; }
-/**************************************************************************** - * WCMD_more - * - * Output either a file or stdin to screen in pages - */ - -RETURN_CODE WCMD_more(WCHAR *args) +static RETURN_CODE page_file(HANDLE h, ULONG64 file_length, BOOL close_previous) { - int argno = 0; - WCHAR *argN = args; - WCHAR moreStr[100]; - WCHAR moreStrPage[100]; - WCHAR buffer[512]; - DWORD count; - RETURN_CODE return_code = NO_ERROR; + WCHAR more_string[100]; + WCHAR page_string[100]; + WCHAR buffer[MAXSTRING]; + RETURN_CODE return_code = NO_ERROR; + DWORD dummy; + BOOL is_output_console = GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &dummy);
- /* Prefix the NLS more with '-- ', then load the text */ - lstrcpyW(moreStr, L"-- "); - LoadStringW(hinst, WCMD_MORESTR, &moreStr[3], ARRAY_SIZE(moreStr)-3); + LoadStringW(hinst, WCMD_MORESTR, more_string, ARRAY_SIZE(more_string));
- if (param1[0] == 0x00) { + wsprintfW(page_string, L"-- %s --", more_string);
- /* Wine implements pipes via temporary files, and hence stdin is - effectively reading from the file. This means the prompts for - more are satisfied by the next line from the input (file). To - avoid this, ensure stdin is to the console */ - HANDLE hstdin = GetStdHandle(STD_INPUT_HANDLE); - HANDLE hConIn = CreateFileW(L"CONIN$", GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ, NULL, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, 0); - WINE_TRACE("No parms - working probably in pipe mode\n"); - SetStdHandle(STD_INPUT_HANDLE, hConIn); - - /* Warning: No easy way of ending the stream (ctrl+z on windows) so - once you get in this bit unless due to a pipe, it's going to end badly... */ - wsprintfW(moreStrPage, L"%s --\n", moreStr); - - WCMD_enter_paged_mode(moreStrPage); - while (WCMD_ReadFile(hstdin, buffer, ARRAY_SIZE(buffer)-1, &count)) { - if (count == 0) break; /* ReadFile reports success on EOF! */ - buffer[count] = 0; - WCMD_output_asis (buffer); + if (close_previous) + { + if (is_output_console) + { + wsprintfW(page_string, L"-- %s (100%%) --", more_string); + WCMD_output_asis(page_string); + WCMD_output_flush(); + } + WCMD_wait_for_console_input(); + if (is_output_console) + WCMD_output_asis(L"\r"); } - WCMD_leave_paged_mode();
- /* Restore stdin to what it was */ - SetStdHandle(STD_INPUT_HANDLE, hstdin); - CloseHandle(hConIn); - WCMD_output_asis (L"\r\n"); - } else { - BOOL needsPause = FALSE; + WCMD_enter_paged_mode(page_string); + while (return_code == NO_ERROR && WCMD_fgets(buffer, ARRAY_SIZE(buffer), h)) + { + LARGE_INTEGER lizero = {.QuadPart = 0}, lipos;
- /* Loop through all args */ - WINE_TRACE("Parms supplied - working through each file\n"); - WCMD_enter_paged_mode(moreStrPage); + if (file_length && SetFilePointerEx(h, lizero, &lipos, FILE_CURRENT)) + wsprintfW(page_string, L"-- %s (%2.2d%%) --", more_string, + min(99, (int)(lipos.QuadPart * 100) / file_length));
- while (argN) { - WCHAR *thisArg = WCMD_parameter (args, argno++, &argN, FALSE, FALSE); - HANDLE h; + WCMD_output_asis(buffer); + WCMD_output_asis(L"\r\n");
- if (!argN) break; + return_code = WCMD_ctrlc_status(); + } + WCMD_leave_paged_mode();
- if (needsPause) { + return return_code; +}
- /* Wait */ - wsprintfW(moreStrPage, L"%s (%2.2d%%) --\n", moreStr, 100); - WCMD_leave_paged_mode(); - WCMD_output_asis(moreStrPage); - WCMD_ReadFile(GetStdHandle(STD_INPUT_HANDLE), buffer, ARRAY_SIZE(buffer), &count); - WCMD_enter_paged_mode(moreStrPage); - } +/**************************************************************************** + * WCMD_more + * + * Output either a file or stdin to screen in pages + */ +RETURN_CODE WCMD_more(WCHAR *args) +{ + int argno = 0; + WCHAR *argN;
+ if (param1[0] == 0x00) + { + WINE_TRACE("No parms - working probably in pipe mode\n"); + page_file(GetStdHandle(STD_INPUT_HANDLE), 0, FALSE); + WCMD_output_asis(L"\r\n"); + } + else + { + RETURN_CODE return_code = NO_ERROR;
- WINE_TRACE("more: Processing arg '%s'\n", wine_dbgstr_w(thisArg)); - h = CreateFileW(thisArg, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, NULL); - if (h == INVALID_HANDLE_VALUE) { - WCMD_print_error (); - WCMD_output_stderr(WCMD_LoadMessage(WCMD_READFAIL), thisArg); - } else { - ULONG64 curPos = 0; - ULONG64 fileLen = 0; - WIN32_FILE_ATTRIBUTE_DATA fileInfo; + /* Loop through all args */ + WINE_TRACE("Parms supplied - working through each file\n"); + + for (argno = 0; return_code == NO_ERROR; argno++) + { + LARGE_INTEGER lizero = {.QuadPart = 0}, lifilelen; + WCHAR *thisArg = WCMD_parameter(args, argno, &argN, FALSE, FALSE); + HANDLE h; + + if (!argN) break;
- /* Get the file size */ - GetFileAttributesExW(thisArg, GetFileExInfoStandard, (void*)&fileInfo); - fileLen = (((ULONG64)fileInfo.nFileSizeHigh) << 32) + fileInfo.nFileSizeLow; + WINE_TRACE("more: Processing arg '%s'\n", wine_dbgstr_w(thisArg)); + h = CreateFileW(thisArg, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (h == INVALID_HANDLE_VALUE) + { + WCMD_print_error(); + WCMD_output_stderr(WCMD_LoadMessage(WCMD_READFAIL), thisArg); + break; + }
- needsPause = TRUE; - while (WCMD_ReadFile(h, buffer, ARRAY_SIZE(buffer)-1, &count)) { - if (count == 0) break; /* ReadFile reports success on EOF! */ - buffer[count] = 0; - curPos += count; + SetFilePointerEx(h, lizero, &lifilelen, FILE_END); + SetFilePointerEx(h, lizero, NULL, FILE_BEGIN);
- /* Update % count (would be used in WCMD_output_asis as prompt) */ - wsprintfW(moreStrPage, L"%s (%2.2d%%) --\n", moreStr, (int) min(99, (curPos * 100)/fileLen)); + return_code = page_file(h, lifilelen.QuadPart, argno != 0);
- WCMD_output_asis (buffer); + CloseHandle(h); } - CloseHandle (h); - } } - - WCMD_leave_paged_mode(); - } - return errorlevel = return_code; + return errorlevel = NO_ERROR; }
/**************************************************************************** diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index 4351533042b..ec0a6f2a066 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -219,6 +219,7 @@ WCHAR *WCMD_strip_quotes(WCHAR *cmd); WCHAR *WCMD_LoadMessage(UINT id); WCHAR *WCMD_strsubstW(WCHAR *start, const WCHAR* next, const WCHAR* insert, int len); RETURN_CODE WCMD_wait_for_input(HANDLE hIn); +RETURN_CODE WCMD_wait_for_console_input(void); BOOL WCMD_ReadFile(const HANDLE hIn, WCHAR *intoBuf, const DWORD maxChars, LPDWORD charsRead); BOOL WCMD_read_console(const HANDLE hInput, WCHAR *inputBuffer, const DWORD inputBufferLength, LPDWORD numRead);
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index d308cdc9731..e3682e4256d 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -638,6 +638,11 @@ RETURN_CODE WCMD_wait_for_input(HANDLE hIn) return return_code; }
+RETURN_CODE WCMD_wait_for_console_input(void) +{ + return WCMD_wait_for_input(console_input); +} + /*************************************************************************** * WCMD_ReadFile * @@ -674,6 +679,8 @@ RETURN_CODE WCMD_output_asis(const WCHAR *message) RETURN_CODE return_code = NO_ERROR; const WCHAR* ptr; HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD dummy; + BOOL is_output_console = GetConsoleMode(handle, &dummy);
if (!message) /* Hack for flushing */ { @@ -692,9 +699,11 @@ RETURN_CODE WCMD_output_asis(const WCHAR *message) if (paged_mode && ++line_count >= max_height - 1) { line_count = 0; - WCMD_output_unbuffered(pagedMessage, -1, handle); + if (is_output_console) + WCMD_output_unbuffered(pagedMessage, -1, handle); return_code = WCMD_wait_for_input(console_input); - WCMD_output_unbuffered(L"\r\n", 2, handle); + if (is_output_console) + WCMD_output_unbuffered(L"\r", 1, handle); } } else
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/builtins.c | 45 +++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 22 deletions(-)
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c index 2db7d3d53aa..ef84bee64ac 100644 --- a/programs/cmd/builtins.c +++ b/programs/cmd/builtins.c @@ -3210,31 +3210,32 @@ RETURN_CODE WCMD_setshow_prompt(void)
RETURN_CODE WCMD_setshow_time(void) { - RETURN_CODE return_code = NO_ERROR; - WCHAR curtime[64], buffer[64]; - DWORD count; - SYSTEMTIME st; + RETURN_CODE return_code = NO_ERROR; + WCHAR curtime[64], buffer[64]; + SYSTEMTIME st;
- if (!*param1) { - GetLocalTime(&st); - if (GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, curtime, ARRAY_SIZE(curtime))) { - WCMD_output (WCMD_LoadMessage(WCMD_CURRENTTIME), curtime); - if (wcsstr(quals, L"/T") == NULL) { - WCMD_output (WCMD_LoadMessage(WCMD_NEWTIME)); - WCMD_output_flush(); - if (WCMD_ReadFile(GetStdHandle(STD_INPUT_HANDLE), buffer, ARRAY_SIZE(buffer), &count) && - count > 2) { - WCMD_output_stderr (WCMD_LoadMessage(WCMD_NYI)); + if (!*param1) + { + GetLocalTime(&st); + if (GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, curtime, ARRAY_SIZE(curtime))) + { + WCMD_output(WCMD_LoadMessage(WCMD_CURRENTTIME), curtime); + if (wcsstr(quals, L"/T") == NULL) + { + WCMD_output(WCMD_LoadMessage(WCMD_NEWTIME)); + WCMD_output_flush(); + if (WCMD_fgets(buffer, ARRAY_SIZE(buffer), GetStdHandle(STD_INPUT_HANDLE))) + WCMD_output_stderr(WCMD_LoadMessage(WCMD_NYI)); + } } - } + else WCMD_print_error(); } - else WCMD_print_error (); - } - else { - return_code = ERROR_INVALID_FUNCTION; - WCMD_output_stderr (WCMD_LoadMessage(WCMD_NYI)); - } - return errorlevel = return_code; + else + { + return_code = ERROR_INVALID_FUNCTION; + WCMD_output_stderr(WCMD_LoadMessage(WCMD_NYI)); + } + return errorlevel = return_code; }
/****************************************************************************
From: Eric Pouech epouech@codeweavers.com
With some improvements: - waiting on an input stream can be interrupted by ctrl-c,
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/wcmdmain.c | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-)
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index e3682e4256d..cf1c627cf81 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -601,26 +601,25 @@ static BOOL has_pending_char_events(HANDLE h) */ RETURN_CODE WCMD_wait_for_input(HANDLE hIn) { + HANDLE h[2] = {hIn, control_c_event}; RETURN_CODE return_code; DWORD oldmode; DWORD count; - WCHAR key; + char key;
- return_code = ERROR_INVALID_FUNCTION; + return_code = ERROR_SIGNAL_PENDING; /* some never returned value */ if (GetConsoleMode(hIn, &oldmode)) { - HANDLE h[2] = {hIn, control_c_event}; - SetConsoleMode(hIn, oldmode & ~ENABLE_LINE_INPUT); FlushConsoleInputBuffer(hIn); - while (return_code == ERROR_INVALID_FUNCTION) + while (return_code == ERROR_SIGNAL_PENDING) { switch (WaitForMultipleObjects(2, h, FALSE, INFINITE)) { case WAIT_OBJECT_0: if (has_pending_char_events(hIn)) return_code = NO_ERROR; - /* will make both hIn no long signaled, and also process the pending input record */ + /* will make both hIn no longer signaled, and also process the pending input record */ FlushConsoleInputBuffer(hIn); break; case WAIT_OBJECT_0 + 1: @@ -631,10 +630,26 @@ RETURN_CODE WCMD_wait_for_input(HANDLE hIn) } SetConsoleMode(hIn, oldmode); } - else if (WCMD_ReadFile(hIn, &key, 1, &count) && count) - return_code = NO_ERROR; else - return_code = ERROR_INVALID_FUNCTION; + { + while (return_code == ERROR_SIGNAL_PENDING) + { + switch (WaitForMultipleObjects(2, h, FALSE, INFINITE)) + { + case WAIT_OBJECT_0: + if (ReadFile(hIn, &key, 1, &count, NULL) && count) + return_code = NO_ERROR; + else + return_code = ERROR_INVALID_FUNCTION; + break; + case WAIT_OBJECT_0 + 1: + return_code = STATUS_CONTROL_C_EXIT; + break; + default: break; + } + } + } + return return_code; }
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/wcmd.h | 3 +-- programs/cmd/wcmdmain.c | 25 ------------------------- 2 files changed, 1 insertion(+), 27 deletions(-)
diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index ec0a6f2a066..6810598142c 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -220,12 +220,11 @@ WCHAR *WCMD_LoadMessage(UINT id); WCHAR *WCMD_strsubstW(WCHAR *start, const WCHAR* next, const WCHAR* insert, int len); RETURN_CODE WCMD_wait_for_input(HANDLE hIn); RETURN_CODE WCMD_wait_for_console_input(void); -BOOL WCMD_ReadFile(const HANDLE hIn, WCHAR *intoBuf, const DWORD maxChars, LPDWORD charsRead); BOOL WCMD_read_console(const HANDLE hInput, WCHAR *inputBuffer, const DWORD inputBufferLength, LPDWORD numRead);
enum read_parse_line {RPL_SUCCESS, RPL_EOF, RPL_SYNTAXERROR}; enum read_parse_line WCMD_ReadAndParseLine(CMD_NODE **output); -void node_dispose_tree(CMD_NODE *cmds); +void node_dispose_tree(CMD_NODE *cmds); RETURN_CODE node_execute(CMD_NODE *node);
RETURN_CODE WCMD_call_batch(const WCHAR *, WCHAR *); diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index cf1c627cf81..46b89544221 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -658,31 +658,6 @@ RETURN_CODE WCMD_wait_for_console_input(void) return WCMD_wait_for_input(console_input); }
-/*************************************************************************** - * WCMD_ReadFile - * - * Read characters in from a console/file, returning result in Unicode - */ -BOOL WCMD_ReadFile(const HANDLE hIn, WCHAR *intoBuf, const DWORD maxChars, LPDWORD charsRead) -{ - DWORD numRead; - char *buffer; - - /* Try to read from console as Unicode */ - if (VerifyConsoleIoHandle(hIn) && ReadConsoleW(hIn, intoBuf, maxChars, charsRead, NULL)) return TRUE; - - /* We assume it's a file handle and read then convert from assumed OEM codepage */ - if (!(buffer = get_file_buffer())) - return FALSE; - - if (!ReadFile(hIn, buffer, maxChars, &numRead, NULL)) - return FALSE; - - *charsRead = MultiByteToWideChar(GetConsoleCP(), 0, buffer, numRead, intoBuf, maxChars); - - return TRUE; -} - /******************************************************************* * WCMD_output_asis *