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.
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@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@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@space@ FAILURE 1025 FAILURE 2 ---- success/failure for START command +--- success/failure for pipes +b +@todo_wine@SUCCESS 0 +@todo_wine@FAILURE 1 +a +@todo_wine@SUCCESS 0 +FAILURE 3 +a +@todo_wine@SUCCESS 0 +@todo_wine@FAILURE 255 +@todo_wine@FAILURE 255 +@todo_wine@--- success/failure for START command @todo_wine@FAILURE 1 foo@space@ SUCCESS 1024 @@ -521,6 +533,11 @@ FAILURE 1 SUCCESS 0 FAILURE 1 FAILURE 1 +--- success/failure for change drive command +@todo_wine@SUCCESS 666 +FAILURE 1 +@todo_wine@SUCCESS 0 +FAILURE 1 --- success/failure for MKDIR,MD command FAILURE 1 SUCCESS 0
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@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 -@todo_wine@SUCCESS 666 +SUCCESS 666 FAILURE 1 -@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; }
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@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@SUCCESS 0 -@todo_wine@FAILURE 1 +FAILURE 1 a @todo_wine@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:
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@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 -@todo_wine@SUCCESS 0 +SUCCESS 0 FAILURE 1 a -@todo_wine@SUCCESS 0 +SUCCESS 0 FAILURE 3 a -@todo_wine@SUCCESS 0 -@todo_wine@FAILURE 255 -@todo_wine@FAILURE 255 -@todo_wine@--- success/failure for START command +SUCCESS 0 +FAILURE 255 +FAILURE 255 +--- success/failure for START command @todo_wine@FAILURE 1 foo@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);
From: Eric Pouech epouech@codeweavers.com
Now using Windows's syntax (and no longer the DOS one).
Signed-off-by: Eric Pouech epouech@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 *);
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@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' ------------ -@todo_wine@Example message [A,B,C]?A@or_broken@choice unavailable +Example message [A,B,C]?A@or_broken@choice unavailable 1@or_broken@9009 -@todo_wine@Example message [A,B,C]?B@or_broken@choice unavailable -@todo_wine@2@or_broken@9009 -@todo_wine@[D,E,F]?F@or_broken@choice unavailable -@todo_wine@3@or_broken@9009 -@todo_wine@[A,B,C,X,Y,Z]?Y@or_broken@choice unavailable -@todo_wine@5@or_broken@9009 -@todo_wine@A@or_broken@choice unavailable -@todo_wine@1@or_broken@9009 -@todo_wine@[a,b,c,A,B,C]?A@or_broken@choice unavailable -@todo_wine@4@or_broken@9009 -@todo_wine@255@or_broken@9009 +Example message [A,B,C]?B@or_broken@choice unavailable +2@or_broken@9009 +[D,E,F]?F@or_broken@choice unavailable +3@or_broken@9009 +[A,B,C,X,Y,Z]?Y@or_broken@choice unavailable +5@or_broken@9009 +A@or_broken@choice unavailable +1@or_broken@9009 +[a,b,c,A,B,C]?A@or_broken@choice unavailable +4@or_broken@9009 +255@or_broken@9009 ------------ Testing variable expansion ------------ ~p0 should be path containing batch file @path@
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