https://bugs.winehq.org/show_bug.cgi?id=27605 describes how cntl-c is not handled well in cmd.exe, this patch allows the cntl-c to be returned in the buffer so that it can processed.
From: Jeff Latimer jeff_lats@hotmail.com
--- programs/cmd/batch.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/programs/cmd/batch.c b/programs/cmd/batch.c index 20ae894dbfd..1cd883c0621 100644 --- a/programs/cmd/batch.c +++ b/programs/cmd/batch.c @@ -223,17 +223,32 @@ WCHAR *WCMD_fgets(WCHAR *buf, DWORD noChars, HANDLE h) DWORD charsRead; BOOL status; DWORD i; + DWORD conmode, oldconmode; + CONSOLE_READCONSOLE_CONTROL control; + + GetConsoleMode(h, &oldconmode); + conmode = oldconmode & ~ENABLE_PROCESSED_INPUT; + SetConsoleMode(h, conmode); + control.nLength = sizeof(CONSOLE_READCONSOLE_CONTROL); + control.nInitialChars = 0; + control.dwCtrlWakeupMask = 0x0008; + control.dwConsoleKeyState = 0;
/* We can't use the native f* functions because of the filename syntax differences between DOS and Unix. Also need to lose the LF (or CRLF) from the line. */
- if (VerifyConsoleIoHandle(h) && ReadConsoleW(h, buf, noChars, &charsRead, NULL) && charsRead) { + if (VerifyConsoleIoHandle(h) && ReadConsoleW(h, buf, noChars, &charsRead, &control) && charsRead) { + SetConsoleMode(h, oldconmode); if (!charsRead) return NULL;
/* Find first EOL */ for (i = 0; i < charsRead; i++) { if (buf[i] == '\n' || buf[i] == '\r') break; + if (buf[i] == 0x03) { + WCMD_output(L"^C\n"); + i = 0; + break;} } } else { @@ -242,6 +257,7 @@ WCHAR *WCMD_fgets(WCHAR *buf, DWORD noChars, HANDLE h) UINT cp; const char *p;
+ SetConsoleMode(h, oldconmode); cp = GetOEMCP(); bufA = xalloc(noChars);
eric pouech (@epo) commented about programs/cmd/batch.c:
DWORD charsRead; BOOL status; DWORD i;
- DWORD conmode, oldconmode;
- CONSOLE_READCONSOLE_CONTROL control;
- GetConsoleMode(h, &oldconmode);
I think this would be better handled by rewriting the code as:
`if (GetConsoleMode(h, &oldmode)) {`
`/* put all console related code here */`
`SetConsoleMode(h, oldmode);`
`}`
`else`
`{`
`/* other handles (file, pipe) */`
eric pouech (@epo) commented about programs/cmd/batch.c:
DWORD i;
DWORD conmode, oldconmode;
CONSOLE_READCONSOLE_CONTROL control;
GetConsoleMode(h, &oldconmode);
conmode = oldconmode & ~ENABLE_PROCESSED_INPUT;
SetConsoleMode(h, conmode);
control.nLength = sizeof(CONSOLE_READCONSOLE_CONTROL);
control.nInitialChars = 0;
control.dwCtrlWakeupMask = 0x0008;
control.dwConsoleKeyState = 0;
/* We can't use the native f* functions because of the filename syntax differences between DOS and Unix. Also need to lose the LF (or CRLF) from the line. */
- if (VerifyConsoleIoHandle(h) && ReadConsoleW(h, buf, noChars, &charsRead, NULL) && charsRead) {
we can get rid of VerifyConsoleHandle() as GetConsoleMode() succeeded
eric pouech (@epo) commented about programs/cmd/batch.c:
control.dwConsoleKeyState = 0;
/* We can't use the native f* functions because of the filename syntax differences between DOS and Unix. Also need to lose the LF (or CRLF) from the line. */
- if (VerifyConsoleIoHandle(h) && ReadConsoleW(h, buf, noChars, &charsRead, NULL) && charsRead) {
if (VerifyConsoleIoHandle(h) && ReadConsoleW(h, buf, noChars, &charsRead, &control) && charsRead) {
SetConsoleMode(h, oldconmode); if (!charsRead) return NULL; /* Find first EOL */ for (i = 0; i < charsRead; i++) { if (buf[i] == '\n' || buf[i] == '\r') break;
if (buf[i] == 0x03) {
WCMD_output(L"^C\n");
I don't see ^C printed on native (Win10)
eric pouech (@epo) commented about programs/cmd/batch.c:
DWORD charsRead; BOOL status; DWORD i;
- DWORD conmode, oldconmode;
- CONSOLE_READCONSOLE_CONTROL control;
- GetConsoleMode(h, &oldconmode);
- conmode = oldconmode & ~ENABLE_PROCESSED_INPUT;
- SetConsoleMode(h, conmode);
- control.nLength = sizeof(CONSOLE_READCONSOLE_CONTROL);
- control.nInitialChars = 0;
- control.dwCtrlWakeupMask = 0x0008;
perhaps use 1u << 0x03 instead (to make it clearer that the intent in ctrl-c) (note one day we might even add tab/shift tab here)
eric pouech (@epo) commented about programs/cmd/batch.c:
/* We can't use the native f* functions because of the filename syntax differences between DOS and Unix. Also need to lose the LF (or CRLF) from the line. */
- if (VerifyConsoleIoHandle(h) && ReadConsoleW(h, buf, noChars, &charsRead, NULL) && charsRead) {
if (VerifyConsoleIoHandle(h) && ReadConsoleW(h, buf, noChars, &charsRead, &control) && charsRead) {
SetConsoleMode(h, oldconmode); if (!charsRead) return NULL; /* Find first EOL */ for (i = 0; i < charsRead; i++) { if (buf[i] == '\n' || buf[i] == '\r') break;
if (buf[i] == 0x03) {
WCMD_output(L"^C\n");
i = 0;
break;}
misplaced curly bracket
only works as expected under user32 console
but complely breaks the unix console
culprit is not the patch, is that conhost is closed with ctrl-c when processed input is turned off
(without the patch on unix console, there are unwanted cursor movement, but after hitting ctrl-c one can still do something; with the patch cmd is "frozen")
there at least two issues to consider:
1. ReadConsole with control on unix console doesn't report ctrl-c 2. cmd shouldn't freeze when the console has been closed
either one of 1 or 2 must be supplied before accepting this patch
apart from that, mostly stylistic remarks
On Thu Mar 20 00:49:02 2025 +0000, eric pouech wrote:
only works as expected under user32 console but complely breaks the unix console culprit is not the patch, is that conhost is closed with ctrl-c when processed input is turned off (without the patch on unix console, there are unwanted cursor movement, but after hitting ctrl-c one can still do something; with the patch cmd is "frozen") there at least two issues to consider:
- ReadConsole with control on unix console doesn't report ctrl-c
- cmd shouldn't freeze when the console has been closed
either one of 1 or 2 must be supplied before accepting this patch apart from that, mostly stylistic remarks
Thanks, I will look more closely at it.