diff --git a/programs/cmd/En.rc b/programs/cmd/En.rc index 2e32c1c5db7..87b0780987b 100644 --- a/programs/cmd/En.rc +++ b/programs/cmd/En.rc @@ -211,6 +211,10 @@ PUSHD.\n" WCMD_MORE, "MORE displays output of files or piped input in pages.\n" + WCMD_CHOICE, "CHOICE displays a text and waits, until the User\n\ +press an allowed Key from a selectable list.\n\ +CHOICE is mainly used to build a menu selection in a batch file.\n" + WCMD_EXIT, "EXIT terminates the current command session and returns\n\ to the operating system or shell from which you invoked cmd.\n" @@ -219,6 +223,7 @@ to the operating system or shell from which you invoked cmd.\n" ATTRIB\t\tShow or change DOS file attributes\n\ CALL\t\tInvoke a batch file from inside another\n\ CD (CHDIR)\tChange current default directory\n\ +CHOICE\t\tWait for an keypress from a selectable list\n\ CLS\t\tClear the console screen\n\ COPY\t\tCopy file\n\ CTTY\t\tChange input/output device\n\ diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c index 8c3f727f964..7c35e6ad6f9 100644 --- a/programs/cmd/builtins.c +++ b/programs/cmd/builtins.c @@ -159,6 +159,174 @@ void WCMD_change_tty (void) { } +/**************************************************************************** + * WCMD_choice + * + */ + +void WCMD_choice (WCHAR * command) { + + static const WCHAR bellW[] = {7,0}; + static const WCHAR commaW[] = {',',0}; + static const WCHAR bracket_open[] = {'[',0}; + static const WCHAR bracket_close[] = {']','?',0}; + WCHAR answer[16]; + WCHAR buffer[16]; + WCHAR *ptr = NULL; + WCHAR *opt_c = NULL; + WCHAR *my_command = NULL; + WCHAR opt_default = 0; + DWORD opt_timeout = 0; + DWORD count; + DWORD oldmode; + DWORD have_console; + BOOL opt_n = FALSE; + BOOL opt_s = FALSE; + + have_console = GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &oldmode); + errorlevel = 0; + + my_command = WCMD_strdupW(WCMD_strtrim_leading_spaces(command)); + if (!my_command) + return; + + ptr = WCMD_strtrim_leading_spaces(my_command); + while (*ptr == '/') { + switch (toupperW(ptr[1])) { + case 'C': + ptr += 2; + /* the colon is optional */ + if (*ptr == ':') + ptr++; + + if (!*ptr || isspaceW(*ptr)) { + WINE_FIXME("bad parameter %s for /C\n", wine_dbgstr_w(ptr)); + HeapFree(GetProcessHeap(), 0, my_command); + return; + } + + /* remember the allowed keys (overwrite previous /C option) */ + opt_c = ptr; + while (*ptr && (!isspaceW(*ptr))) + ptr++; + + if (*ptr) { + /* terminate allowed chars */ + *ptr = 0; + ptr = WCMD_strtrim_leading_spaces(&ptr[1]); + } + WINE_TRACE("answer-list: %s\n", wine_dbgstr_w(opt_c)); + break; + + case 'N': + opt_n = TRUE; + ptr = WCMD_strtrim_leading_spaces(&ptr[2]); + break; + + case 'S': + opt_s = TRUE; + ptr = WCMD_strtrim_leading_spaces(&ptr[2]); + 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) : ""); + HeapFree(GetProcessHeap(), 0, my_command); + return; + } + ptr++; + + count = 0; + while (((answer[count] = *ptr)) && isdigitW(*ptr) && (count < 15)) { + count++; + ptr++; + } + + answer[count] = 0; + opt_timeout = atoiW(answer); + + ptr = WCMD_strtrim_leading_spaces(ptr); + break; + + default: + WINE_FIXME("bad parameter: %s\n", wine_dbgstr_w(ptr)); + HeapFree(GetProcessHeap(), 0, my_command); + return; + } + } + + if (opt_timeout) + WINE_FIXME("timeout not supported: %c,%d\n", opt_default, opt_timeout); + + 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, sizeof(buffer)/sizeof(WCHAR)); + LoadStringW(hinst, WCMD_NO, buffer + 1, sizeof(buffer)/sizeof(WCHAR) - 1); + opt_c = buffer; + buffer[2] = 0; + } + + /* print the question, when needed */ + if (*ptr) + WCMD_output_asis(ptr); + + if (!opt_s) { + struprW(opt_c); + WINE_TRACE("case insensitive answer-list: %s\n", wine_dbgstr_w(opt_c)); + } + + if (!opt_n) { + /* print a list of all allowed answers inside brackets */ + WCMD_output_asis(bracket_open); + ptr = opt_c; + answer[1] = 0; + while ((answer[0] = *ptr++)) { + WCMD_output_asis(answer); + if (*ptr) + WCMD_output_asis(commaW); + } + WCMD_output_asis(bracket_close); + } + + while (TRUE) { + + /* FIXME: Add support for option /T */ + WCMD_ReadFile(GetStdHandle(STD_INPUT_HANDLE), answer, 1, &count, NULL); + + if (!opt_s) + answer[0] = toupperW(answer[0]); + + ptr = strchrW(opt_c, answer[0]); + if (ptr) { + WCMD_output_asis(answer); + WCMD_output(newline); + if (have_console) + SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), oldmode); + + errorlevel = (ptr - opt_c) + 1; + WINE_TRACE("answer: %d\n", errorlevel); + HeapFree(GetProcessHeap(), 0, my_command); + return; + } + else + { + /* key not allowed: play the bell */ + WINE_TRACE("key not allowed: %s\n", wine_dbgstr_w(answer)); + WCMD_output_asis(bellW); + } + } +} + /**************************************************************************** * WCMD_copy * diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index 2fde678d5ff..ce4c2da2cd1 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -52,6 +52,7 @@ void WCMD_assoc (WCHAR *, BOOL); void WCMD_batch (WCHAR *, WCHAR *, int, WCHAR *, HANDLE); void WCMD_call (WCHAR *command); void WCMD_change_tty (void); +void WCMD_choice (WCHAR *); void WCMD_clear_screen (void); void WCMD_color (void); void WCMD_copy (void); @@ -202,9 +203,10 @@ typedef struct _DIRECTORY_STACK #define WCMD_COLOR 41 #define WCMD_FTYPE 42 #define WCMD_MORE 43 +#define WCMD_CHOICE 44 /* Must be last in list */ -#define WCMD_EXIT 44 +#define WCMD_EXIT 45 /* Some standard messages */ extern const WCHAR newline[]; diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index 85f78b88f2c..7b7ce8ffde8 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -76,6 +76,7 @@ const WCHAR inbuilt[][10] = { {'C','O','L','O','R','\0'}, {'F','T','Y','P','E','\0'}, {'M','O','R','E','\0'}, + {'C','H','O','I','C','E','\0'}, {'E','X','I','T','\0'} }; @@ -1543,6 +1544,9 @@ void WCMD_execute (WCHAR *command, WCHAR *redirects, case WCMD_MORE: WCMD_more(p); break; + case WCMD_CHOICE: + WCMD_choice(p); + break; case WCMD_EXIT: WCMD_exit (cmdList); break;