This is part XXIII of cmd engine rewrite. This MR covers: - some fixes from previous patches, - some preparation for revisiting external/builtin commands
From: Eric Pouech epouech@codeweavers.com
As Wine's cmd.exe doesn't have a message loop, don't show dialog boxes from ShellExecute in case of error.
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/wcmdmain.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index af38fc7f04e..c6fa13f9739 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -1428,7 +1428,14 @@ static RETURN_CODE run_full_path(const WCHAR *file, WCHAR *full_cmdline, BOOL ca WCHAR *args;
WCMD_parameter(full_cmdline, 1, &args, FALSE, TRUE); - sei.fMask = SEE_MASK_NO_CONSOLE | SEE_MASK_NOCLOSEPROCESS; + /* FIXME: when the file extension is not registered, + * native cmd does popup a dialog box to register an app for this extension. + * Also, ShellExecuteW returns before the dialog box is closed. + * Moreover, on Wine, displaying a dialog box without a message loop + * (in cmd.exe) blocks the dialog. + * So, until the above bits are solved, don't display any dialog box. + */ + sei.fMask = SEE_MASK_NO_CONSOLE | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI; sei.lpFile = file; sei.lpParameters = args; sei.nShow = SW_SHOWNORMAL;
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/tests/test_builtins.cmd | 25 +++++++++++++++++++++--- programs/cmd/tests/test_builtins.cmd.exp | 5 +++++ 2 files changed, 27 insertions(+), 3 deletions(-)
diff --git a/programs/cmd/tests/test_builtins.cmd b/programs/cmd/tests/test_builtins.cmd index e4e4d1b37df..8b4c8a92ca7 100644 --- a/programs/cmd/tests/test_builtins.cmd +++ b/programs/cmd/tests/test_builtins.cmd @@ -490,6 +490,19 @@ call :setError 666 & (Idontexist.exe &&echo SUCCESS !errorlevel!||echo FAILURE ! call :setError 666 & Idontexist.exe & echo ERRORLEVEL !errorlevel! call :setError 666 & (cmd.exe /c "echo foo & exit /b 0" &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) call :setError 666 & (cmd.exe /c "echo foo & exit /b 1024" &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (I\dont\exist.html &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +rem native cmd on Windows 7 shows a dialog for missing association, and waits for its dismisal... +rem later native cmd.exe shows the dialog, but is not blocking. so, skip the Windows7 case +rem FIRMWARE_TYPE (undocumented readonly dynamic env variable) appears from Windows 8 onwards +set foobar_doskip= +if not defined WINEPREFIX if not defined FIRMWARE_TYPE (set foobar_doskip=1) +if defined foobar_doskip ( + echo Skipping on Win7 + set foobar_doskip= +) else ( + echo:>foobar.IDontExist + call :setError 666 & (foobar.IDontExist &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +) cd .. && rd /q /s foo echo --- success/failure for CALL command mkdir foo & cd foo @@ -520,9 +533,7 @@ call :setError 666 & (start "" /foobar >NUL &&echo SUCCESS !errorlevel!||echo FA rem call :setError 666 & (start /B I\dont\exist.exe &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) rem can't run this test, generates a nice popup under windows call :setError 666 & (start "" /B /WAIT cmd.exe /c "echo foo & exit /b 1024" &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) -rem A call :setError 666 & (start "" /B cmd.exe /c "(choice /C:YN /T:3 /D:Y > NUL) & exit /b 1024" &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) -rem can't do on Wine until /T is properly handled in CHOICE -rem SUCCESS 666 +call :setError 666 & (start "" /B cmd.exe /c "(choice /C:YN /T:3 /D:Y > NUL) & exit /b 1024" &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) echo --- success/failure for TYPE command mkdir foo & cd foo echo a > fileA @@ -1073,6 +1084,14 @@ setlocal EnableDelayedExpansion set WINE_FOO=foo bar for %%i in ("!WINE_FOO!") do echo %%i for %%i in (!WINE_FOO!) do echo %%i + +setlocal DisableDelayedExpansion + +set "BEFORE_DELAY=before!" +setlocal EnableDelayedExpansion +set "AFTER_DELAY=after^!" +echo !BEFORE_DELAY! +echo !AFTER_DELAY! setlocal DisableDelayedExpansion
echo --- in digit variables diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp index f57336358d4..803047dd166 100644 --- a/programs/cmd/tests/test_builtins.cmd.exp +++ b/programs/cmd/tests/test_builtins.cmd.exp @@ -477,6 +477,8 @@ foo@space@ SUCCESS 0 foo@space@ FAILURE 1024 +FAILURE 1 +@todo_wine@SUCCESS 666@or_broken@Skipping on Win7 --- success/failure for CALL command FAILURE 1 SUCCESS 0 @@ -501,6 +503,7 @@ FAILURE 255 @todo_wine@FAILURE 1 foo@space@ SUCCESS 1024 +@todo_wine@SUCCESS 666 --- success/failure for TYPE command FAILURE 1 SUCCESS 0 @@ -828,6 +831,8 @@ N "foo bar" foo bar +before! +@todo_wine@after! --- in digit variables a %1 %2 b %1 %2
From: Eric Pouech epouech@codeweavers.com
Fixes syntax errors from using 'in' word in command block of a FOR intruction.
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/wcmdmain.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index c6fa13f9739..231e1aaac3f 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -2922,9 +2922,11 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** /* In a for loop, the DO command will follow a close bracket followed by whitespace, followed by DO, ie closeBracket inserts a NULL entry, curLen is then 0, and all whitespace is skipped */ - } else if (inFor && WCMD_keyword_ws_found(L"do", curPos)) { + } else if (inFor && lastWasIn && WCMD_keyword_ws_found(L"do", curPos)) {
WINE_TRACE("Found 'DO '\n"); + inFor = FALSE; + lastWasIn = FALSE; lastWasDo = TRUE; acceptCommand = TRUE; onlyWhiteSpace = TRUE;
From: Eric Pouech epouech@codeweavers.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=57017
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/builtins.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c index 5a7ad82f783..149d34571e5 100644 --- a/programs/cmd/builtins.c +++ b/programs/cmd/builtins.c @@ -408,6 +408,7 @@ RETURN_CODE WCMD_choice(WCHAR *args) LARGE_INTEGER li, zeroli = {0}; OVERLAPPED overlapped = {0}; DWORD count; + char choice;
overlapped.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); if (SetFilePointerEx(GetStdHandle(STD_INPUT_HANDLE), zeroli, &li, FILE_CURRENT)) @@ -415,11 +416,12 @@ RETURN_CODE WCMD_choice(WCHAR *args) overlapped.Offset = li.LowPart; overlapped.OffsetHigh = li.HighPart; } - if (ReadFile(GetStdHandle(STD_INPUT_HANDLE), answer, 1, NULL, &overlapped)) + if (ReadFile(GetStdHandle(STD_INPUT_HANDLE), &choice, 1, NULL, &overlapped)) { switch (WaitForSingleObject(overlapped.hEvent, opt_timeout == -1 ? INFINITE : opt_timeout * 1000)) { case WAIT_OBJECT_0: + answer[0] = choice; break; case WAIT_TIMEOUT: answer[0] = opt_default; @@ -428,7 +430,7 @@ RETURN_CODE WCMD_choice(WCHAR *args) return_code = ERROR_INVALID_FUNCTION; } } - else if (ReadFile(GetStdHandle(STD_INPUT_HANDLE), answer, 1, &count, NULL)) + else if (ReadFile(GetStdHandle(STD_INPUT_HANDLE), &choice, 1, &count, NULL)) { if (count == 0) { @@ -437,6 +439,8 @@ RETURN_CODE WCMD_choice(WCHAR *args) else return_code = ERROR_INVALID_FUNCTION; } + else + answer[0] = choice; } else return_code = ERROR_INVALID_FUNCTION;
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/wcmdmain.c | 49 +++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 26 deletions(-)
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index 231e1aaac3f..19ccd52f82f 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -467,13 +467,16 @@ BOOL WCMD_get_fullpath(const WCHAR* in, SIZE_T outsize, WCHAR* out, WCHAR** star * Return a pointer to the first non-whitespace character of string. * Does not modify the input string. */ -WCHAR *WCMD_skip_leading_spaces (WCHAR *string) { - - WCHAR *ptr; +WCHAR *WCMD_skip_leading_spaces(WCHAR *string) +{ + while (*string == L' ' || *string == L'\t') string++; + return string; +}
- ptr = string; - while (*ptr == ' ' || *ptr == '\t') ptr++; - return ptr; +static WCHAR *WCMD_strip_for_command_start(WCHAR *string) +{ + while (*string == L' ' || *string == L'\t' || *string == L'@') string++; + return string; }
/*************************************************************************** @@ -1753,34 +1756,28 @@ static RETURN_CODE execute_single_command(const WCHAR *command) RETURN_CODE return_code; WCHAR *cmd, *parms_start; int cmd_index, count; - WCHAR *whichcmd; - WCHAR *new_cmd = NULL; BOOL prev_echo_mode;
TRACE("command on entry:%s\n", wine_dbgstr_w(command));
/* Move copy of the command onto the heap so it can be expanded */ - new_cmd = xalloc(MAXSTRING * sizeof(WCHAR)); - lstrcpyW(new_cmd, command); - cmd = new_cmd; + cmd = xalloc(MAXSTRING * sizeof(WCHAR)); + lstrcpyW(cmd, command);
- /* Strip leading whitespaces, and a '@' if supplied */ - whichcmd = WCMD_skip_leading_spaces(cmd); TRACE("Command: '%s'\n", wine_dbgstr_w(cmd)); - if (whichcmd[0] == '@') whichcmd++;
/* Check if the command entered is internal, and identify which one */ count = 0; - while (IsCharAlphaNumericW(whichcmd[count])) { + while (IsCharAlphaNumericW(cmd[count])) { count++; } for (cmd_index=0; cmd_index<=WCMD_EXIT; cmd_index++) { if (count && CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT, - whichcmd, count, inbuilt[cmd_index], -1) == CSTR_EQUAL) break; + cmd, count, inbuilt[cmd_index], -1) == CSTR_EQUAL) break; } - parms_start = WCMD_skip_leading_spaces (&whichcmd[count]); + parms_start = WCMD_skip_leading_spaces(&cmd[count]);
- handleExpansion(new_cmd, TRUE); + handleExpansion(cmd, TRUE);
/* * Changing default drive has to be handled as a special case, anything @@ -1813,13 +1810,13 @@ static RETURN_CODE execute_single_command(const WCHAR *command) goto cleanup; }
- WCMD_parse (parms_start, quals, param1, param2); + WCMD_parse(parms_start, quals, param1, param2); TRACE("param1: %s, param2: %s\n", wine_dbgstr_w(param1), wine_dbgstr_w(param2));
if (cmd_index <= WCMD_EXIT && (parms_start[0] == '/') && (parms_start[1] == '?')) { /* this is a help request for a builtin program */ cmd_index = WCMD_HELP; - memcpy(parms_start, whichcmd, count * sizeof(WCHAR)); + memcpy(parms_start, cmd, count * sizeof(WCHAR)); parms_start[count] = '\0'; }
@@ -1849,7 +1846,7 @@ static RETURN_CODE execute_single_command(const WCHAR *command) return_code = WCMD_directory(parms_start); break; case WCMD_ECHO: - return_code = WCMD_echo(&whichcmd[count]); + return_code = WCMD_echo(&cmd[count]); break; case WCMD_GOTO: return_code = WCMD_goto(); @@ -1949,7 +1946,7 @@ static RETURN_CODE execute_single_command(const WCHAR *command) break; default: prev_echo_mode = echo_mode; - return_code = WCMD_run_program(whichcmd, FALSE); + return_code = WCMD_run_program(cmd, FALSE); echo_mode = prev_echo_mode; }
@@ -2779,8 +2776,6 @@ static WCHAR *fetch_next_line(BOOL feed, BOOL first_line, WCHAR* buffer) WCMD_output_asis(L"\r\n"); }
- /* Skip repeated 'no echo' characters and whitespace */ - while (*buffer == '@' || *buffer == L' ' || *buffer == L'\t') buffer++; return buffer; }
@@ -2848,6 +2843,7 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** lastWasRedirect = FALSE; /* Required e.g. for spaces between > and filename */ onlyWhiteSpace = TRUE;
+ curPos = WCMD_strip_for_command_start(curPos); /* Parse every character on the line being processed */ while (*curPos != 0x00) {
@@ -2866,6 +2862,8 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **
/* Certain commands need special handling */ if (curStringLen == 0 && curCopyTo == curString) { + if (acceptCommand) + curPos = WCMD_strip_for_command_start(curPos); /* If command starts with 'rem ' or identifies a label, ignore any &&, ( etc. */ if (WCMD_keyword_ws_found(L"rem", curPos) || *curPos == ':') { inOneLine = TRUE; @@ -3187,8 +3185,7 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** do { curPos = fetch_next_line(TRUE, FALSE, extraSpace); } while (curPos && *curPos == L'\0'); - if (!curPos) - curPos = extraSpace; + curPos = curPos ? WCMD_strip_for_command_start(curPos) : extraSpace; } } }
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=148107
Your paranoid android.
=== debian11b (64 bit WoW report) ===
kernel32: comm.c:1586: Test failed: Unexpected time 1000, expected around 500
user32: input.c:4305: Test succeeded inside todo block: button_down_hwnd_todo 1: got MSG_TEST_WIN hwnd 00000000013900F2, msg WM_LBUTTONDOWN, wparam 0x1, lparam 0x320032