[PATCH v2 0/2] MR7491: cmd: Introduce MODE command
Like BUG-57912 and BUG-57913, this was encountered in https://www.smwcentral.net/?p=section&a=details&id=33546 (.resources/Lunar Magic/scripts/insert_all_patches.bat). Unfortunately, I was unable to write a passing test; winetest redirects the child's stdout to a file, so GetConsoleScreenBufferInfo fails. And some of those numbers will vary between machines. Should I just drop the test? -- v2: cmd/tests: Add tests for new MODE command. cmd: Introduce MODE command. https://gitlab.winehq.org/wine/wine/-/merge_requests/7491
From: Alfred Agrell <floating(a)muncher.se> --- programs/cmd/builtins.c | 91 +++++++++++++++++++++++++++++++++++++++++ programs/cmd/cmd.rc | 3 ++ programs/cmd/wcmd.h | 36 ++++++++-------- programs/cmd/wcmdmain.c | 3 ++ 4 files changed, 116 insertions(+), 17 deletions(-) diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c index d7c5090d17f..502cfe9ec38 100644 --- a/programs/cmd/builtins.c +++ b/programs/cmd/builtins.c @@ -72,6 +72,7 @@ const WCHAR inbuilt[][10] = { L"START", L"TIME", L"TITLE", + L"MODE", L"TYPE", L"VERIFY", L"VER", @@ -3417,6 +3418,96 @@ RETURN_CODE WCMD_title(const WCHAR *args) return NO_ERROR; } +/**************************************************************************** + * WCMD_mode + * + * Get or set the console size + */ +static RETURN_CODE WCMD_mode_con(WCHAR *args); + +RETURN_CODE WCMD_mode(WCHAR *args) +{ + WCHAR *device = WCMD_parameter(args, 0, NULL, FALSE, FALSE); + + if (!*device) + { + /* just print everything (which is CON only for now) */ + WCMD_mode_con(args); + errorlevel = 0; + } + else if (!wcsicmp(device, L"CON")) + { + errorlevel = WCMD_mode_con(args); + } + else + { + WCMD_output(L"Invalid parameter - %1\r\n", device); + errorlevel = -1; + } + return errorlevel; +} + +static RETURN_CODE WCMD_mode_con(WCHAR *args) +{ + CONSOLE_SCREEN_BUFFER_INFO inf; + UINT keybd_speed, keybd_delay; + BOOL changed = FALSE; + WCHAR buf[1024]; + HANDLE console; + WCHAR *argN; + int argno; + + errorlevel = 0; + console = CreateFileW(L"CONOUT$", GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + SystemParametersInfoA(SPI_GETKEYBOARDSPEED, 0, &keybd_speed, 0); + SystemParametersInfoA(SPI_GETKEYBOARDDELAY, 0, &keybd_delay, 0); + GetConsoleScreenBufferInfo(console, &inf); + + for (argno = 1; ; argno++) + { + WCHAR *arg = WCMD_parameter_with_delims(args, argno, &argN, FALSE, FALSE, L" "); + if (!*arg) break; + + if (!wcsnicmp(arg, L"COLS=", 5) && (inf.dwSize.X = wcstoul(arg+5, NULL, 10))) + changed = TRUE; + else if (!wcsnicmp(arg, L"LINES=", 6) && (inf.dwSize.Y = wcstoul(arg+6, NULL, 10))) + changed = TRUE; + else + { + WCMD_output(L"Invalid parameter - %1\r\n", arg); + errorlevel = -1; + } + } + + if (changed) + { + if (!SetConsoleScreenBufferSize(console, inf.dwSize)) + { + WCMD_output_asis(L"The screen cannot be set to the number of lines and columns specified.\r\n"); + errorlevel = -1; + } + } + else if (errorlevel == 0) + { + wsprintfW(buf, + L"\r\n" + L"Status for device CON:\r\n" + L"----------------------\r\n" + L" Lines: %u\r\n" + L" Columns: %u\r\n" + L" Keyboard rate: %u\r\n" + L" Keyboard delay: %u\r\n" + L" Code page: %u\r\n" + L"\r\n", + inf.dwSize.Y, inf.dwSize.X, keybd_speed, keybd_delay, GetConsoleCP()); + WCMD_output_asis(buf); + } + CloseHandle(console); + return errorlevel; +} + /**************************************************************************** * WCMD_type * diff --git a/programs/cmd/cmd.rc b/programs/cmd/cmd.rc index 90091090e11..83598bdc4d7 100644 --- a/programs/cmd/cmd.rc +++ b/programs/cmd/cmd.rc @@ -235,6 +235,9 @@ called from the command line.\n" WCMD_TITLE, "TITLE <string> sets the window title for the cmd window.\n" + WCMD_MODE, +"MODE tells the cmd window size.\n" + WCMD_TYPE, "TYPE <filename> copies <filename> to the console device (or elsewhere if\n\ redirected). No check is made that the file is readable text.\n" diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index 225ebd44310..86d8cb40921 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -192,6 +192,7 @@ RETURN_CODE WCMD_setshow_time(void); RETURN_CODE WCMD_shift(const WCHAR *args); RETURN_CODE WCMD_start(WCHAR *args); RETURN_CODE WCMD_title(const WCHAR *); +RETURN_CODE WCMD_mode(WCHAR *); RETURN_CODE WCMD_type(WCHAR *); RETURN_CODE WCMD_verify(void); RETURN_CODE WCMD_version(void); @@ -378,25 +379,26 @@ static inline BOOL WCMD_is_in_context(const WCHAR *ext) #define WCMD_START 29 #define WCMD_TIME 30 #define WCMD_TITLE 31 -#define WCMD_TYPE 32 -#define WCMD_VERIFY 33 -#define WCMD_VER 34 -#define WCMD_VOL 35 - -#define WCMD_ENDLOCAL 36 -#define WCMD_SETLOCAL 37 -#define WCMD_PUSHD 38 -#define WCMD_POPD 39 -#define WCMD_ASSOC 40 -#define WCMD_COLOR 41 -#define WCMD_FTYPE 42 -#define WCMD_MORE 43 -#define WCMD_CHOICE 44 -#define WCMD_MKLINK 45 -#define WCMD_CHGDRIVE 46 +#define WCMD_MODE 32 +#define WCMD_TYPE 33 +#define WCMD_VERIFY 34 +#define WCMD_VER 35 +#define WCMD_VOL 36 + +#define WCMD_ENDLOCAL 37 +#define WCMD_SETLOCAL 38 +#define WCMD_PUSHD 39 +#define WCMD_POPD 40 +#define WCMD_ASSOC 41 +#define WCMD_COLOR 42 +#define WCMD_FTYPE 43 +#define WCMD_MORE 44 +#define WCMD_CHOICE 45 +#define WCMD_MKLINK 46 +#define WCMD_CHGDRIVE 47 /* Must be last in list */ -#define WCMD_EXIT 47 +#define WCMD_EXIT 48 /* Some standard messages */ extern WCHAR anykey[]; diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index 77f1112be23..792e35689e8 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -1789,6 +1789,9 @@ RETURN_CODE WCMD_run_builtin_command(int cmd_index, WCHAR *cmd) case WCMD_TIME: return_code = WCMD_setshow_time(); break; + case WCMD_MODE: + return_code = WCMD_mode(parms_start); + break; case WCMD_TITLE: return_code = WCMD_title(parms_start); break; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/7491
From: Alfred Agrell <floating(a)muncher.se> --- programs/cmd/tests/test_builtins.cmd | 18 ++++++++++++++++-- programs/cmd/tests/test_builtins.cmd.exp | 13 +++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/programs/cmd/tests/test_builtins.cmd b/programs/cmd/tests/test_builtins.cmd index 1253e5fc2a5..8b2794216ee 100644 --- a/programs/cmd/tests/test_builtins.cmd +++ b/programs/cmd/tests/test_builtins.cmd @@ -3173,7 +3173,7 @@ regedit /s regCleanup.reg set WINE_FOO= endlocal cd .. & rd /s/q foobar -goto ContinueCall +goto ExitFtype :SkipFType echo --- setting association echo --- @@ -3190,8 +3190,22 @@ echo Skipped as not enough permissions echo Skipped as not enough permissions echo --- resetting association echo original value +:ExitFtype + +echo ------------ Testing mode ------------ +call :setError 666 & (mode CON >nul &&echo SUCCESS||echo FAILURE) +echo %errorlevel% +call :setError 666 & (mode CON | findstr "666" >nul &&echo SUCCESS||echo FAILURE) +echo %errorlevel% +call :setError 666 & (mode CON lines=666 &&echo SUCCESS||echo FAILURE) +echo %errorlevel% +call :setError 666 & (mode CON | findstr "666" >nul &&echo SUCCESS||echo FAILURE) +echo %errorlevel% +call :setError 666 & (mode CON lines=false >nul &&echo SUCCESS||echo FAILURE) +echo %errorlevel% +call :setError 666 & (mode NOTADEVICE >nul &&echo SUCCESS||echo FAILURE) +echo %errorlevel% -:ContinueCall echo ------------ Testing CALL ------------ mkdir foobar & cd foobar echo --- external script diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp index 6137c594359..57906df021a 100644 --- a/programs/cmd/tests/test_builtins.cmd.exp +++ b/programs/cmd/tests/test_builtins.cmd.exp @@ -1861,6 +1861,19 @@ footype=cmd.exe /c "echo '%*'" foobar(a)or_broken@Skipped as not enough permissions --- resetting association original value(a)or_broken@buggyXP(a)or_broken@!WINE_FOO! +------------ Testing mode ------------ +SUCCESS +0 +FAILURE +1 +SUCCESS +0 +SUCCESS +0 +FAILURE +-1 +FAILURE +-1 ------------ Testing CALL ------------ --- external script foo(a)space@ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/7491
eric pouech (@epo) commented about programs/cmd/builtins.c:
+ WCMD_output(L"Invalid parameter - %1\r\n", arg); + errorlevel = -1; + } + } + + if (changed) + { + if (!SetConsoleScreenBufferSize(console, inf.dwSize)) + { + WCMD_output_asis(L"The screen cannot be set to the number of lines and columns specified.\r\n"); + errorlevel = -1; + } + } + else if (errorlevel == 0) + { + wsprintfW(buf, this should be moved as a string into the .rc file for localisation
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/7491#note_97390
eric pouech (@epo) commented about programs/cmd/builtins.c:
+ int argno; + + errorlevel = 0; + console = CreateFileW(L"CONOUT$", GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + SystemParametersInfoA(SPI_GETKEYBOARDSPEED, 0, &keybd_speed, 0); + SystemParametersInfoA(SPI_GETKEYBOARDDELAY, 0, &keybd_delay, 0); + GetConsoleScreenBufferInfo(console, &inf); + + for (argno = 1; ; argno++) + { + WCHAR *arg = WCMD_parameter_with_delims(args, argno, &argN, FALSE, FALSE, L" "); + if (!*arg) break; + + if (!wcsnicmp(arg, L"COLS=", 5) && (inf.dwSize.X = wcstoul(arg+5, NULL, 10))) native fails if lhs is not only made of numbers... (eg cols=12a fails unlike this code)
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/7491#note_97389
eric pouech (@epo) commented about programs/cmd/builtins.c:
L"START", L"TIME", L"TITLE", + L"MODE",
any reason to break the existing lexical order of builtin commands? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/7491#note_97387
eric pouech (@epo) commented about programs/cmd/builtins.c:
+ } + return errorlevel; +} + +static RETURN_CODE WCMD_mode_con(WCHAR *args) +{ + CONSOLE_SCREEN_BUFFER_INFO inf; + UINT keybd_speed, keybd_delay; + BOOL changed = FALSE; + WCHAR buf[1024]; + HANDLE console; + WCHAR *argN; + int argno; + + errorlevel = 0; + console = CreateFileW(L"CONOUT$", GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, what if creation fails?
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/7491#note_97388
eric pouech (@epo) commented about programs/cmd/builtins.c:
return NO_ERROR; }
+/**************************************************************************** + * WCMD_mode + * + * Get or set the console size + */ +static RETURN_CODE WCMD_mode_con(WCHAR *args);
just move function implementation here to avoid forward declaration -- https://gitlab.winehq.org/wine/wine/-/merge_requests/7491#note_97386
eric pouech (@epo) commented about programs/cmd/builtins.c:
+ errorlevel = -1; + } + return errorlevel; +} + +static RETURN_CODE WCMD_mode_con(WCHAR *args) +{ + CONSOLE_SCREEN_BUFFER_INFO inf; + UINT keybd_speed, keybd_delay; + BOOL changed = FALSE; + WCHAR buf[1024]; + HANDLE console; + WCHAR *argN; + int argno; + + errorlevel = 0; for consistency with other bultins:
* I'd rather use a local variable RETURN_CODE return_code * return that variable from the builtin command * and set errorlevel only at the end of the function, depending on return_code value Note: this is the first time we have a -1 errorlevel, so we have have some other surprises -- https://gitlab.winehq.org/wine/wine/-/merge_requests/7491#note_97391
On Tue Mar 11 10:23:06 2025 +0000, eric pouech wrote:
just move function implementation here to avoid forward declaration I felt that having the entry point on top makes more sense, but if you feel otherwise, then sure.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/7491#note_97392
On Tue Mar 11 10:23:06 2025 +0000, eric pouech wrote:
what if creation fails? Is that possible?
...probably if it's running in a service or CREATE_NO_WINDOW or something. Sure, can check. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/7491#note_97394
On Tue Mar 11 10:23:06 2025 +0000, eric pouech wrote:
for consistency with other bultins: * I'd rather use a local variable RETURN_CODE return_code * return that variable from the builtin command * and set errorlevel only at the end of the function, depending on return_code value Note: this is the first time we have a -1 errorlevel, so we have have some other surprises Sure, works for me. As long as finding those surprises isn't my responsibility.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/7491#note_97397
On Tue Mar 11 10:23:06 2025 +0000, eric pouech wrote:
any reason to break the existing lexical order of builtin commands? The order is broken already.
Everything after VOL is completely unordered, RD comes after RENAME, and there's a blank string for some reason, so I guessed they're sorted more by purpose than alphabetically. MODE felt similar to TITLE. Makes no difference to me, I can move it. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/7491#note_97393
On Tue Mar 11 10:23:06 2025 +0000, eric pouech wrote:
native fails if lhs is not only made of numbers... (eg cols=12a fails unlike this code) I'm sure that's a very plausible input
but sure, let's fix it -- https://gitlab.winehq.org/wine/wine/-/merge_requests/7491#note_97395
On Tue Mar 11 10:23:06 2025 +0000, eric pouech wrote:
this should be moved as a string into the .rc file for localisation I'm sure localization would break approximately 999999 badly written batch scripts
but sure, why not, those are broken on native too -- https://gitlab.winehq.org/wine/wine/-/merge_requests/7491#note_97396
participants (3)
-
Alfred Agrell -
Alfred Agrell (@Alcaro) -
eric pouech (@epo)