[PATCH 0/6] MR6090: Cmd engine rewrite (part XXI)
This is part XX1 of cmd engine rewrite. It covers: - tests & fixes for success/failure of changing current drive, - tests & fixes for success/failure when using pipes, - new implementation of CHOICE command. Notes: - CHOICE is not a builtin command in native cmd.exe, but I kept it builtin for simplicity, - CHOICE was (partially) implemented using syntax from MS-DOS days; it has been moved to use Windows' syntax. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/6090
From: Eric Pouech <epouech(a)codeweavers.com> Signed-off-by: Eric Pouech <epouech(a)codeweavers.com> --- programs/cmd/tests/test_builtins.cmd | 22 ++++++++++++++++++++-- programs/cmd/tests/test_builtins.cmd.exp | 21 +++++++++++++++++++-- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/programs/cmd/tests/test_builtins.cmd b/programs/cmd/tests/test_builtins.cmd index c67fc84988a..945b68e25a9 100644 --- a/programs/cmd/tests/test_builtins.cmd +++ b/programs/cmd/tests/test_builtins.cmd @@ -467,12 +467,13 @@ if 1==0 (echo q1) else echo q2&echo q3 echo ------------- Testing internal commands return codes setlocal EnableDelayedExpansion -echo --- call and IF/FOR blocks +echo --- success/failure for basics call :setError 0 &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel! call :setError 33 &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel! call :setError 666 & (echo foo &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) call :setError 666 & (echo foo >> h:\i\dont\exist\at\all.txt &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) call :setError 666 & echo foo >> h:\i\dont\exist\at\all.txt & echo ERRORLEVEL !errorlevel! +echo --- success/failure for IF/FOR blocks call :setError 666 & ((if 1==1 echo "">NUL) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) call :setError 666 & ((if 1==0 echo "">NUL) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) call :setError 666 & ((if 1==1 (call :setError 33)) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) @@ -481,7 +482,6 @@ call :setError 666 & ((for %%i in () do echo "") &&echo SUCCESS !errorlevel!||ec call :setError 666 & ((for %%i in () do call :setError 33) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) call :setError 666 & ((for %%i in (a) do call :setError 0) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) call :setError 666 & ((for %%i in (a) do call :setError 33) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) - echo --- success/failure for external command mkdir foo & cd foo call :setError 666 & (I\dont\exist.exe &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) @@ -503,6 +503,18 @@ call :setError 666 & (call cmd.exe /c "echo foo & exit /b 0" &&echo SUCCESS !err call :setError 666 & (call cmd.exe /c "echo foo & exit /b 1025" &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) call :setError 666 & (call rmdir foobar.dir &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) cd .. && rd /q /s foo +echo --- success/failure for pipes +call :setError 666 & ((echo a | echo b) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & ((echo a | call :setError 34) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & ((call :setError 33 | echo a) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & ((echo a | rmdir I\dont\exist\at\all) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & ((rmdir I\dont\exist\at\all | echo a) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +rem in a pipe, if LHS or RHS can't be started, the whole cmd is abandonned (not just the pipe!!) +echo ^( %%1 ^| %%2 ^) > foo.bat +echo echo AFTER %%ERRORLEVEL%% >> foo.bat +call :setError 666 & (cmd.exe /q /c "call foo.bat echo I\dont\exist.exe" &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (cmd.exe /q /c "call foo.bat I\dont\exist.exe echo" &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +erase /q foo.bat echo --- success/failure for START command call :setError 666 & (start "" /foobar >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) rem call :setError 666 & (start /B I\dont\exist.exe &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) @@ -568,6 +580,12 @@ call :setError 666 & (erase i\dont\exist\at\all.txt &&echo SUCCESS !errorlevel!| call :setError 666 & (erase file* i\dont\exist\at\all.txt &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) cd .. && rd /q /s foo +echo --- success/failure for change drive command +call :setError 666 & (c: &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (1: &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (call c: &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (call 1: &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) + echo --- success/failure for MKDIR,MD command mkdir foo & cd foo call :setError 666 & (mkdir &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp index 84bcdcdf581..58b4dcfb00e 100644 --- a/programs/cmd/tests/test_builtins.cmd.exp +++ b/programs/cmd/tests/test_builtins.cmd.exp @@ -452,13 +452,14 @@ p2 q2 q3 ------------- Testing internal commands return codes ---- call and IF/FOR blocks +--- success/failure for basics SUCCESS 0 FAILURE 33 foo(a)space@ SUCCESS 666 FAILURE 1 ERRORLEVEL 666 +--- success/failure for IF/FOR blocks SUCCESS 666 SUCCESS 666 FAILURE 33 @@ -485,7 +486,18 @@ SUCCESS 0 foo(a)space@ FAILURE 1025 FAILURE 2 ---- success/failure for START command +--- success/failure for pipes +b +(a)todo_wine@SUCCESS 0 +(a)todo_wine@FAILURE 1 +a +(a)todo_wine@SUCCESS 0 +FAILURE 3 +a +(a)todo_wine@SUCCESS 0 +(a)todo_wine@FAILURE 255 +(a)todo_wine@FAILURE 255 +(a)todo_wine@--- success/failure for START command @todo_wine(a)FAILURE 1 foo(a)space@ SUCCESS 1024 @@ -521,6 +533,11 @@ FAILURE 1 SUCCESS 0 FAILURE 1 FAILURE 1 +--- success/failure for change drive command +(a)todo_wine@SUCCESS 666 +FAILURE 1 +(a)todo_wine@SUCCESS 0 +FAILURE 1 --- success/failure for MKDIR,MD command FAILURE 1 SUCCESS 0 -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/6090
From: Eric Pouech <epouech(a)codeweavers.com> Signed-off-by: Eric Pouech <epouech(a)codeweavers.com> --- programs/cmd/tests/test_builtins.cmd.exp | 4 ++-- programs/cmd/wcmdmain.c | 18 ++++++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp index 58b4dcfb00e..0ad3b0c80bd 100644 --- a/programs/cmd/tests/test_builtins.cmd.exp +++ b/programs/cmd/tests/test_builtins.cmd.exp @@ -534,9 +534,9 @@ SUCCESS 0 FAILURE 1 FAILURE 1 --- success/failure for change drive command -(a)todo_wine@SUCCESS 666 +SUCCESS 666 FAILURE 1 -(a)todo_wine@SUCCESS 0 +SUCCESS 0 FAILURE 1 --- success/failure for MKDIR,MD command FAILURE 1 diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index 5f55e4c8d9e..dd49f2ab65d 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -1732,7 +1732,7 @@ static RETURN_CODE execute_single_command(const WCHAR *command) { RETURN_CODE return_code; WCHAR *cmd, *parms_start; - int status, cmd_index, count; + int cmd_index, count; WCHAR *whichcmd; WCHAR *new_cmd = NULL; BOOL prev_echo_mode; @@ -1767,27 +1767,29 @@ static RETURN_CODE execute_single_command(const WCHAR *command) * else if it exists after whitespace is ignored */ - if ((cmd[1] == ':') && IsCharAlphaW(cmd[0]) && - (!cmd[2] || cmd[2] == ' ' || cmd[2] == '\t')) { + if (cmd[1] == L':' && (!cmd[2] || iswspace(cmd[2]))) { WCHAR envvar[5]; WCHAR dir[MAX_PATH]; /* Ignore potential garbage on the same line */ - cmd[2]=0x00; + cmd[2] = L'\0'; /* According to MSDN CreateProcess docs, special env vars record the current directory on each drive, in the form =C: so see if one specified, and if so go back to it */ lstrcpyW(envvar, L"="); lstrcatW(envvar, cmd); - if (GetEnvironmentVariableW(envvar, dir, MAX_PATH) == 0) { + if (GetEnvironmentVariableW(envvar, dir, ARRAY_SIZE(dir)) == 0) { wsprintfW(cmd, L"%s\\", cmd); WINE_TRACE("No special directory settings, using dir of %s\n", wine_dbgstr_w(cmd)); } WINE_TRACE("Got directory %s as %s\n", wine_dbgstr_w(envvar), wine_dbgstr_w(cmd)); - status = SetCurrentDirectoryW(cmd); - if (!status) WCMD_print_error (); - return_code = ERROR_INVALID_FUNCTION; + if (!SetCurrentDirectoryW(cmd)) + { + WCMD_print_error(); + return_code = errorlevel = ERROR_INVALID_FUNCTION; + } + else return_code = NO_ERROR; goto cleanup; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/6090
From: Eric Pouech <epouech(a)codeweavers.com> Signed-off-by: Eric Pouech <epouech(a)codeweavers.com> --- programs/cmd/tests/test_builtins.cmd.exp | 2 +- programs/cmd/wcmdmain.c | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp index 0ad3b0c80bd..33d4609f289 100644 --- a/programs/cmd/tests/test_builtins.cmd.exp +++ b/programs/cmd/tests/test_builtins.cmd.exp @@ -489,7 +489,7 @@ FAILURE 2 --- success/failure for pipes b @todo_wine(a)SUCCESS 0 -(a)todo_wine@FAILURE 1 +FAILURE 1 a @todo_wine(a)SUCCESS 0 FAILURE 3 diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index dd49f2ab65d..e1c8802f6e8 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -3700,7 +3700,10 @@ RETURN_CODE node_execute(CMD_NODE *node) WCHAR filename[MAX_PATH]; CMD_REDIRECTION *output; HANDLE saved_output; + BATCH_CONTEXT *saved_context = context; + /* pipe LHS & RHS are run outside of any batch context */ + context = NULL; /* FIXME: a real pipe instead of writing to an intermediate file would be * better. * But waiting for completion of commands will require more work. @@ -3743,6 +3746,7 @@ RETURN_CODE node_execute(CMD_NODE *node) } else return_code = ERROR_INVALID_FUNCTION; redirection_dispose_list(output); + context = saved_context; } break; case CMD_IF: -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/6090
From: Eric Pouech <epouech(a)codeweavers.com> Signed-off-by: Eric Pouech <epouech(a)codeweavers.com> --- programs/cmd/tests/test_builtins.cmd.exp | 12 ++++++------ programs/cmd/wcmdmain.c | 13 +++++++------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp index 33d4609f289..5d9629e35a9 100644 --- a/programs/cmd/tests/test_builtins.cmd.exp +++ b/programs/cmd/tests/test_builtins.cmd.exp @@ -488,16 +488,16 @@ FAILURE 1025 FAILURE 2 --- success/failure for pipes b -(a)todo_wine@SUCCESS 0 +SUCCESS 0 FAILURE 1 a -(a)todo_wine@SUCCESS 0 +SUCCESS 0 FAILURE 3 a -(a)todo_wine@SUCCESS 0 -(a)todo_wine@FAILURE 255 -(a)todo_wine@FAILURE 255 -(a)todo_wine@--- success/failure for START command +SUCCESS 0 +FAILURE 255 +FAILURE 255 +--- success/failure for START command @todo_wine(a)FAILURE 1 foo(a)space@ SUCCESS 1024 diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index e1c8802f6e8..d3ad93837d4 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -3723,12 +3723,13 @@ RETURN_CODE node_execute(CMD_NODE *node) if (set_std_redirections(output)) { RETURN_CODE return_code_left = node_execute(node->left); - return_code = NO_ERROR; - CloseHandle(GetStdHandle(STD_OUTPUT_HANDLE)); SetStdHandle(STD_OUTPUT_HANDLE, saved_output); - if (return_code == NO_ERROR) + if (errorlevel == RETURN_CODE_CANT_LAUNCH && saved_context) + ExitProcess(255); + return_code = ERROR_INVALID_FUNCTION; + if (return_code_left != RETURN_CODE_ABORTED && errorlevel != RETURN_CODE_CANT_LAUNCH) { HANDLE h = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING, @@ -3737,12 +3738,12 @@ RETURN_CODE node_execute(CMD_NODE *node) { SetStdHandle(STD_INPUT_HANDLE, h); return_code = node_execute(node->right); + if (errorlevel == RETURN_CODE_CANT_LAUNCH && saved_context) + ExitProcess(255); } - else return_code = ERROR_INVALID_FUNCTION; } DeleteFileW(filename); - if (return_code_left != NO_ERROR || return_code != NO_ERROR) - errorlevel = ERROR_INVALID_FUNCTION; + errorlevel = return_code; } else return_code = ERROR_INVALID_FUNCTION; redirection_dispose_list(output); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/6090
From: Eric Pouech <epouech(a)codeweavers.com> Now using Windows's syntax (and no longer the DOS one). Signed-off-by: Eric Pouech <epouech(a)codeweavers.com> --- programs/cmd/builtins.c | 201 +++++++++++++++++++++------------------- programs/cmd/wcmd.h | 2 +- 2 files changed, 106 insertions(+), 97 deletions(-) diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c index 17671c32eba..46c1d9cc874 100644 --- a/programs/cmd/builtins.c +++ b/programs/cmd/builtins.c @@ -279,147 +279,154 @@ RETURN_CODE WCMD_clear_screen(void) * */ -RETURN_CODE WCMD_choice (const WCHAR * args) +RETURN_CODE WCMD_choice(WCHAR *args) { + RETURN_CODE return_code = NO_ERROR; WCHAR answer[16]; WCHAR buffer[16]; WCHAR *ptr = NULL; WCHAR *opt_c = NULL; - WCHAR *my_command = NULL; + WCHAR *opt_m = NULL; WCHAR opt_default = 0; - DWORD opt_timeout = 0; + DWORD opt_timeout = -1; + WCHAR *end; DWORD count; DWORD oldmode; BOOL have_console; BOOL opt_n = FALSE; - BOOL opt_s = FALSE; - - have_console = GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &oldmode); - my_command = xstrdupW(WCMD_skip_leading_spaces((WCHAR*)args)); - - ptr = my_command; - /* syntax errors are reported with ERRORLEVEL=1, which doesn't allow to - * discriminate from a choosen option! - */ - while (*ptr == '/') { - switch (towupper(ptr[1])) { - case 'C': - ptr += 2; - /* the colon is optional */ - if (*ptr == ':') - ptr++; - - if (!*ptr || iswspace(*ptr)) { - WINE_FIXME("bad parameter %s for /C\n", wine_dbgstr_w(ptr)); - free(my_command); - return errorlevel = ERROR_INVALID_FUNCTION; - } + BOOL opt_cs = FALSE; + int argno; - /* remember the allowed keys (overwrite previous /C option) */ - opt_c = ptr; - while (*ptr && (!iswspace(*ptr))) - ptr++; + for (argno = 0; ; argno++) + { + WCHAR *arg = WCMD_parameter(args, argno, NULL, FALSE, FALSE); + if (!*arg) break; - if (*ptr) { - /* terminate allowed chars */ - *ptr = 0; - ptr = WCMD_skip_leading_spaces(&ptr[1]); + if (!wcsicmp(arg, L"/N")) opt_n = TRUE; + else if (!wcsicmp(arg, L"/CS")) opt_cs = TRUE; + else if (arg[0] == L'/' && wcschr(L"CDTM", towupper(arg[1]))) + { + WCHAR opt = towupper(arg[1]); + if (arg[2] == L'\0') + { + arg = WCMD_parameter(args, ++argno, NULL, FALSE, FALSE); + if (!*arg) + { + return_code = ERROR_INVALID_FUNCTION; + break; } - WINE_TRACE("answer-list: %s\n", wine_dbgstr_w(opt_c)); + } + else if (arg[2] == L':') + arg += 3; + else + { + return_code = ERROR_INVALID_FUNCTION; break; - - case 'N': - opt_n = TRUE; - ptr = WCMD_skip_leading_spaces(&ptr[2]); + } + switch (opt) + { + case L'C': + opt_c = wcsdup(arg); break; - - case 'S': - opt_s = TRUE; - ptr = WCMD_skip_leading_spaces(&ptr[2]); + case L'M': + opt_m = wcsdup(arg); break; - - case 'T': - ptr = &ptr[2]; - /* the colon is optional */ - if (*ptr == ':') - ptr++; - - opt_default = *ptr++; - - if (!opt_default || (*ptr != ',')) { - WINE_FIXME("bad option %s for /T\n", opt_default ? wine_dbgstr_w(ptr) : ""); - free(my_command); - return errorlevel = ERROR_INVALID_FUNCTION; - } - ptr++; - - count = 0; - while (((answer[count] = *ptr)) && iswdigit(*ptr) && (count < 15)) { - count++; - ptr++; - } - - answer[count] = 0; - opt_timeout = wcstol(answer, NULL, 10); - - ptr = WCMD_skip_leading_spaces(ptr); + case L'D': + opt_default = *arg; break; - - default: - WINE_FIXME("bad parameter: %s\n", wine_dbgstr_w(ptr)); - free(my_command); - return errorlevel = ERROR_INVALID_FUNCTION; + case L'T': + opt_timeout = wcstol(arg, &end, 10); + if (end == arg || (*end && !iswspace(*end))) + opt_timeout = 10000; + break; + } } + else + return_code = ERROR_INVALID_FUNCTION; } - if (opt_timeout) - WINE_FIXME("timeout not supported: %c,%ld\n", opt_default, opt_timeout); + /* use default keys, when needed: localized versions of "Y"es and "No" */ + if (!opt_c) + { + LoadStringW(hinst, WCMD_YES, buffer, ARRAY_SIZE(buffer)); + LoadStringW(hinst, WCMD_NO, buffer + 1, ARRAY_SIZE(buffer) - 1); + opt_c = buffer; + buffer[2] = L'\0'; + } + /* validate various options */ + if (!opt_cs) wcsupr(opt_c); + /* check that default is in the choices list */ + if (!wcschr(opt_c, opt_cs ? opt_default : towupper(opt_default))) + return_code = ERROR_INVALID_FUNCTION; + /* check that there's no duplicates in the choices list */ + for (ptr = opt_c; *ptr; ptr++) + if (wcschr(ptr + 1, opt_cs ? *ptr : towupper(*ptr))) + return_code = ERROR_INVALID_FUNCTION; + TRACE("CHOICE message(%s) choices(%s) timeout(%ld) default(%lc)\n", + debugstr_w(opt_m), debugstr_w(opt_c), opt_timeout, opt_default ? opt_default : L'?'); + if (return_code != NO_ERROR || + (opt_timeout == -1) != (opt_default == L'\0') || + (opt_timeout != -1 && opt_timeout > 9999)) + { + WCMD_output_stderr(WCMD_LoadMessage(WCMD_ARGERR)); + if (return_code == NO_ERROR) return_code = ERROR_INVALID_FUNCTION; + if (opt_c != buffer) free(opt_c); + free(opt_m); + return errorlevel = return_code; + } + if (opt_timeout != -1) + { + WINE_FIXME("timeout not supported: %c,%ld\n", opt_default, opt_timeout); + return errorlevel = ERROR_INVALID_FUNCTION; + } + have_console = GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &oldmode); if (have_console) SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), 0); /* use default keys, when needed: localized versions of "Y"es and "No" */ - if (!opt_c) { + if (!opt_c) + { LoadStringW(hinst, WCMD_YES, buffer, ARRAY_SIZE(buffer)); LoadStringW(hinst, WCMD_NO, buffer + 1, ARRAY_SIZE(buffer) - 1); opt_c = buffer; - buffer[2] = 0; + buffer[2] = L'\0'; } /* print the question, when needed */ - if (*ptr) - WCMD_output_asis(ptr); + if (opt_m) + WCMD_output_asis(opt_m); - if (!opt_s) { + if (!opt_cs) wcsupr(opt_c); - WINE_TRACE("case insensitive answer-list: %s\n", wine_dbgstr_w(opt_c)); - } - if (!opt_n) { + if (!opt_n) + { /* print a list of all allowed answers inside brackets */ WCMD_output_asis(L"["); - ptr = opt_c; - answer[1] = 0; - while ((answer[0] = *ptr++)) { - WCMD_output_asis(answer); - if (*ptr) + answer[1] = L'\0'; + for (ptr = opt_c; *ptr; ptr++) + { + if (ptr != opt_c) WCMD_output_asis(L","); + answer[0] = *ptr; + WCMD_output_asis(answer); } WCMD_output_asis(L"]?"); } - while (TRUE) { - + while (TRUE) + { /* FIXME: Add support for option /T */ answer[1] = 0; /* terminate single character string */ if (!WCMD_ReadFile(GetStdHandle(STD_INPUT_HANDLE), answer, 1, &count) || !count) { - free(my_command); /* FIXME: is this choice 1 or ERROR_INVALID_FUNCTION? */ - return errorlevel = 1; + return_code = 1; + break; } - if (!opt_s) + if (!opt_cs) answer[0] = towupper(answer[0]); ptr = wcschr(opt_c, answer[0]); @@ -429,10 +436,9 @@ RETURN_CODE WCMD_choice (const WCHAR * args) if (have_console) SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), oldmode); - errorlevel = (ptr - opt_c) + 1; + return_code = (ptr - opt_c) + 1; TRACE("answer: %d\n", errorlevel); - free(my_command); - return errorlevel; + break; } else { @@ -441,6 +447,9 @@ RETURN_CODE WCMD_choice (const WCHAR * args) WCMD_output_asis(L"\a"); } } + if (opt_c != buffer) free(opt_c); + free(opt_m); + return errorlevel = return_code; } /**************************************************************************** diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index 8e870993b06..c800da59821 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -157,7 +157,7 @@ BOOL WCMD_print_volume_information(const WCHAR *); RETURN_CODE WCMD_assoc(const WCHAR *, BOOL); RETURN_CODE WCMD_batch(const WCHAR *, WCHAR *, const WCHAR *, HANDLE); RETURN_CODE WCMD_call(WCHAR *command); -RETURN_CODE WCMD_choice(const WCHAR *); +RETURN_CODE WCMD_choice(WCHAR *); RETURN_CODE WCMD_clear_screen(void); RETURN_CODE WCMD_color(void); RETURN_CODE WCMD_copy(WCHAR *); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/6090
From: Eric Pouech <epouech(a)codeweavers.com> Signed-off-by: Eric Pouech <epouech(a)codeweavers.com> --- programs/cmd/builtins.c | 93 +++++++++++++++--------- programs/cmd/tests/test_builtins.cmd.exp | 24 +++--- 2 files changed, 70 insertions(+), 47 deletions(-) diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c index 46c1d9cc874..fa23b143768 100644 --- a/programs/cmd/builtins.c +++ b/programs/cmd/builtins.c @@ -290,7 +290,6 @@ RETURN_CODE WCMD_choice(WCHAR *args) WCHAR opt_default = 0; DWORD opt_timeout = -1; WCHAR *end; - DWORD count; DWORD oldmode; BOOL have_console; BOOL opt_n = FALSE; @@ -370,39 +369,24 @@ RETURN_CODE WCMD_choice(WCHAR *args) (opt_timeout != -1 && opt_timeout > 9999)) { WCMD_output_stderr(WCMD_LoadMessage(WCMD_ARGERR)); - if (return_code == NO_ERROR) return_code = ERROR_INVALID_FUNCTION; + errorlevel = 255; if (opt_c != buffer) free(opt_c); free(opt_m); - return errorlevel = return_code; - } - if (opt_timeout != -1) - { - WINE_FIXME("timeout not supported: %c,%ld\n", opt_default, opt_timeout); - return errorlevel = ERROR_INVALID_FUNCTION; + return ERROR_INVALID_FUNCTION; } + have_console = GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &oldmode); if (have_console) SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), 0); - /* use default keys, when needed: localized versions of "Y"es and "No" */ - if (!opt_c) - { - LoadStringW(hinst, WCMD_YES, buffer, ARRAY_SIZE(buffer)); - LoadStringW(hinst, WCMD_NO, buffer + 1, ARRAY_SIZE(buffer) - 1); - opt_c = buffer; - buffer[2] = L'\0'; - } - /* print the question, when needed */ if (opt_m) WCMD_output_asis(opt_m); - if (!opt_cs) - wcsupr(opt_c); - if (!opt_n) { /* print a list of all allowed answers inside brackets */ + if (opt_m) WCMD_output_asis(L" "); WCMD_output_asis(L"["); answer[1] = L'\0'; for (ptr = opt_c; *ptr; ptr++) @@ -415,41 +399,80 @@ RETURN_CODE WCMD_choice(WCHAR *args) WCMD_output_asis(L"]?"); } - while (TRUE) + while (return_code == NO_ERROR) { - /* FIXME: Add support for option /T */ - answer[1] = 0; /* terminate single character string */ - if (!WCMD_ReadFile(GetStdHandle(STD_INPUT_HANDLE), answer, 1, &count) || !count) + if (opt_timeout == 0) + answer[0] = opt_default; + else + { + LARGE_INTEGER li, zeroli = {0}; + OVERLAPPED overlapped = {0}; + DWORD count; + + overlapped.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); + if (SetFilePointerEx(GetStdHandle(STD_INPUT_HANDLE), zeroli, &li, FILE_CURRENT)) + { + overlapped.Offset = li.LowPart; + overlapped.OffsetHigh = li.HighPart; + } + if (ReadFile(GetStdHandle(STD_INPUT_HANDLE), answer, 1, NULL, &overlapped)) + { + switch (WaitForSingleObject(overlapped.hEvent, opt_timeout == -1 ? INFINITE : opt_timeout * 1000)) + { + case WAIT_OBJECT_0: + break; + case WAIT_TIMEOUT: + answer[0] = opt_default; + break; + default: + return_code = ERROR_INVALID_FUNCTION; + } + } + else if (ReadFile(GetStdHandle(STD_INPUT_HANDLE), answer, 1, &count, NULL)) + { + if (count == 0) + { + if (opt_timeout != -1) + answer[0] = opt_default; + else + return_code = ERROR_INVALID_FUNCTION; + } + } + else + return_code = ERROR_INVALID_FUNCTION; + CloseHandle(overlapped.hEvent); + } + if (return_code != NO_ERROR) { - /* FIXME: is this choice 1 or ERROR_INVALID_FUNCTION? */ - return_code = 1; + errorlevel = 255; break; } - if (!opt_cs) answer[0] = towupper(answer[0]); + answer[1] = L'\0'; /* terminate single character string */ ptr = wcschr(opt_c, answer[0]); - if (ptr) { + if (ptr) + { WCMD_output_asis(answer); WCMD_output_asis(L"\r\n"); - if (have_console) - SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), oldmode); - return_code = (ptr - opt_c) + 1; - TRACE("answer: %d\n", errorlevel); - break; + return_code = errorlevel = (ptr - opt_c) + 1; + TRACE("answer: %d\n", return_code); } else { /* key not allowed: play the bell */ - WINE_TRACE("key not allowed: %s\n", wine_dbgstr_w(answer)); + TRACE("key not allowed: %s\n", wine_dbgstr_w(answer)); WCMD_output_asis(L"\a"); } } + if (have_console) + SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), oldmode); + if (opt_c != buffer) free(opt_c); free(opt_m); - return errorlevel = return_code; + return return_code; } /**************************************************************************** diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp index 5d9629e35a9..03bcb674bdb 100644 --- a/programs/cmd/tests/test_builtins.cmd.exp +++ b/programs/cmd/tests/test_builtins.cmd.exp @@ -707,19 +707,19 @@ I'm here!@space@ I'm here!@space@ I'm here!@space@ ------------ Testing 'choice' ------------ -(a)todo_wine@Example message [A,B,C]?A(a)or_broken@choice unavailable +Example message [A,B,C]?A(a)or_broken@choice unavailable 1(a)or_broken@9009 -(a)todo_wine@Example message [A,B,C]?B(a)or_broken@choice unavailable -(a)todo_wine@2(a)or_broken@9009 -(a)todo_wine@[D,E,F]?F(a)or_broken@choice unavailable -(a)todo_wine@3(a)or_broken@9009 -(a)todo_wine@[A,B,C,X,Y,Z]?Y(a)or_broken@choice unavailable -(a)todo_wine@5(a)or_broken@9009 -(a)todo_wine@A(a)or_broken@choice unavailable -(a)todo_wine@1(a)or_broken@9009 -(a)todo_wine@[a,b,c,A,B,C]?A(a)or_broken@choice unavailable -(a)todo_wine@4(a)or_broken@9009 -(a)todo_wine@255(a)or_broken@9009 +Example message [A,B,C]?B(a)or_broken@choice unavailable +2(a)or_broken@9009 +[D,E,F]?F(a)or_broken@choice unavailable +3(a)or_broken@9009 +[A,B,C,X,Y,Z]?Y(a)or_broken@choice unavailable +5(a)or_broken@9009 +A(a)or_broken@choice unavailable +1(a)or_broken@9009 +[a,b,c,A,B,C]?A(a)or_broken@choice unavailable +4(a)or_broken@9009 +255(a)or_broken@9009 ------------ Testing variable expansion ------------ ~p0 should be path containing batch file @path@ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/6090
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 full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=147174 Your paranoid android. === build (build log) === error: patch failed: programs/cmd/tests/test_builtins.cmd.exp:489 error: patch failed: programs/cmd/wcmdmain.c:3700 error: patch failed: programs/cmd/tests/test_builtins.cmd.exp:488 error: patch failed: programs/cmd/wcmdmain.c:3723 error: patch failed: programs/cmd/builtins.c:279 error: patch failed: programs/cmd/wcmd.h:157 error: patch failed: programs/cmd/builtins.c:290 error: patch failed: programs/cmd/tests/test_builtins.cmd.exp:707 Task: Patch failed to apply === debian11 (build log) === error: patch failed: programs/cmd/tests/test_builtins.cmd.exp:489 error: patch failed: programs/cmd/wcmdmain.c:3700 error: patch failed: programs/cmd/tests/test_builtins.cmd.exp:488 error: patch failed: programs/cmd/wcmdmain.c:3723 error: patch failed: programs/cmd/builtins.c:279 error: patch failed: programs/cmd/wcmd.h:157 error: patch failed: programs/cmd/builtins.c:290 error: patch failed: programs/cmd/tests/test_builtins.cmd.exp:707 Task: Patch failed to apply === debian11b (build log) === error: patch failed: programs/cmd/tests/test_builtins.cmd.exp:489 error: patch failed: programs/cmd/wcmdmain.c:3700 error: patch failed: programs/cmd/tests/test_builtins.cmd.exp:488 error: patch failed: programs/cmd/wcmdmain.c:3723 error: patch failed: programs/cmd/builtins.c:279 error: patch failed: programs/cmd/wcmd.h:157 error: patch failed: programs/cmd/builtins.c:290 error: patch failed: programs/cmd/tests/test_builtins.cmd.exp:707 Task: Patch failed to apply
participants (3)
-
Eric Pouech -
eric pouech (@epo) -
Marvin