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.
From: Alfred Agrell floating@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;
From: Alfred Agrell floating@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@or_broken@Skipped as not enough permissions --- resetting association original value@or_broken@buggyXP@or_broken@!WINE_FOO! +------------ Testing mode ------------ +SUCCESS +0 +FAILURE +1 +SUCCESS +0 +SUCCESS +0 +FAILURE +-1 +FAILURE +-1 ------------ Testing CALL ------------ --- external script foo@space@
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
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?
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?
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)
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
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
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.
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.
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.
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
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
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.