tool_doswin: Restore original console settings on CTRL signal

- Move Windows terminal init code from tool_main to tool_doswin.

- Restore the original console settings on CTRL+C and CTRL+BREAK.

Background: On Windows the curl tool changes the console settings to
enable virtual terminal processing (eg color output) if supported
(ie Win 10). The original settings are restored on exit but prior to
this change were not restored in the case of the CTRL signals.

Windows VT behavior varies depending on console/powershell/terminal;
refer to the discussion in #6226.

Assisted-by: Rich Turner

Closes https://github.com/curl/curl/pull/6226
This commit is contained in:
Jay Satiro 2020-11-19 16:49:24 -05:00
parent 33993d45fe
commit 3831043eff
2 changed files with 66 additions and 53 deletions

View File

@ -28,6 +28,7 @@
#endif
#ifdef WIN32
# include <stdlib.h>
# include <tlhelp32.h>
# include "tool_cfgable.h"
# include "tool_libinfo.h"
@ -702,6 +703,64 @@ cleanup:
return slist;
}
/* The terminal settings to restore on exit */
static struct TerminalSettings {
HANDLE hStdOut;
DWORD dwOutputMode;
LONG valid;
} TerminalSettings;
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
#endif
static void restore_terminal(void)
{
if(InterlockedExchange(&TerminalSettings.valid, (LONG)FALSE))
SetConsoleMode(TerminalSettings.hStdOut, TerminalSettings.dwOutputMode);
}
/* This is the console signal handler.
* The system calls it in a separate thread.
*/
static BOOL WINAPI signal_handler(DWORD type)
{
if(type == CTRL_C_EVENT || type == CTRL_BREAK_EVENT)
restore_terminal();
return FALSE;
}
static void init_terminal(void)
{
TerminalSettings.hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
/*
* Enable VT (Virtual Terminal) output.
* Note: VT mode flag can be set on any version of Windows, but VT
* processing only performed on Win10 >= Creators Update)
*/
if((TerminalSettings.hStdOut != INVALID_HANDLE_VALUE) &&
GetConsoleMode(TerminalSettings.hStdOut,
&TerminalSettings.dwOutputMode) &&
!(TerminalSettings.dwOutputMode &
ENABLE_VIRTUAL_TERMINAL_PROCESSING)) {
/* The signal handler is set before attempting to change the console mode
because otherwise a signal would not be caught after the change but
before the handler was installed. */
(void)InterlockedExchange(&TerminalSettings.valid, (LONG)TRUE);
if(SetConsoleCtrlHandler(signal_handler, TRUE)) {
if(SetConsoleMode(TerminalSettings.hStdOut,
(TerminalSettings.dwOutputMode |
ENABLE_VIRTUAL_TERMINAL_PROCESSING))) {
atexit(restore_terminal);
}
else {
SetConsoleCtrlHandler(signal_handler, FALSE);
(void)InterlockedExchange(&TerminalSettings.valid, (LONG)FALSE);
}
}
}
}
LARGE_INTEGER tool_freq;
bool tool_isVistaOrGreater;
@ -714,6 +773,9 @@ CURLcode win32_init(void)
tool_isVistaOrGreater = false;
QueryPerformanceFrequency(&tool_freq);
init_terminal();
return CURLE_OK;
}

View File

@ -23,6 +23,10 @@
#include <sys/stat.h>
#ifdef WIN32
#include <tchar.h>
#endif
#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif
@ -225,51 +229,6 @@ static void main_free(struct GlobalConfig *config)
config->last = NULL;
}
#ifdef WIN32
/* TerminalSettings for Windows */
static struct TerminalSettings {
HANDLE hStdOut;
DWORD dwOutputMode;
} TerminalSettings;
static void configure_terminal(void)
{
/*
* If we're running Windows, enable VT output.
* Note: VT mode flag can be set on any version of Windows, but VT
* processing only performed on Win10 >= Creators Update)
*/
/* Define the VT flags in case we're building with an older SDK */
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
#endif
memset(&TerminalSettings, 0, sizeof(TerminalSettings));
/* Enable VT output */
TerminalSettings.hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
if((TerminalSettings.hStdOut != INVALID_HANDLE_VALUE)
&& (GetConsoleMode(TerminalSettings.hStdOut,
&TerminalSettings.dwOutputMode))) {
SetConsoleMode(TerminalSettings.hStdOut,
TerminalSettings.dwOutputMode
| ENABLE_VIRTUAL_TERMINAL_PROCESSING);
}
}
#else
#define configure_terminal()
#endif
static void restore_terminal(void)
{
#ifdef WIN32
/* Restore Console output mode and codepage to whatever they were
* when Curl started */
SetConsoleMode(TerminalSettings.hStdOut, TerminalSettings.dwOutputMode);
#endif
}
/*
** curl tool main function.
*/
@ -284,7 +243,6 @@ int main(int argc, char *argv[])
memset(&global, 0, sizeof(global));
#ifdef WIN32
#ifdef _tcscmp
/* Undocumented diagnostic option to list the full paths of all loaded
modules. This is purposely pre-init. */
if(argc == 2 && !_tcscmp(argv[1], _T("--dump-module-paths"))) {
@ -294,7 +252,6 @@ int main(int argc, char *argv[])
curl_slist_free_all(head);
return head ? 0 : 1;
}
#endif /* _tcscmp */
/* win32_init must be called before other init routines. */
result = win32_init();
if(result) {
@ -303,9 +260,6 @@ int main(int argc, char *argv[])
}
#endif
/* Perform any platform-specific terminal configuration */
configure_terminal();
main_checkfds();
#if defined(HAVE_SIGNAL) && defined(SIGPIPE)
@ -326,9 +280,6 @@ int main(int argc, char *argv[])
main_free(&global);
}
/* Return the terminal to its original state */
restore_terminal();
#ifdef __NOVELL_LIBC__
if(getenv("_IN_NETWARE_BASH_") == NULL)
tool_pressanykey();