terminal: teach git how to save/restore its terminal settings
Currently, git will share its console with all its children (unless they create their own), and is therefore possible that any of them that might change the settings for it could affect its operations once completed. Refactor the platform specific functionality to save the terminal settings and expand it to also do so for the output handler. This will allow for the state of the terminal to be saved and restored around a child that might misbehave (ex vi) which will be implemented next. Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Carlo Marcelo Arenas Belón committed
Oct 5, 2021 at 00:46 UTC
e22b245ea576bf1787284971b467f45f45045f97
2 files changed
+63
-15
compat/terminal.c
+60
-15
@@ -8,8 +8,6 @@
8
9
#if defined(HAVE_DEV_TTY) || defined(GIT_WINDOWS_NATIVE)
10
11
-static void restore_term(void);
12
-
11
static void restore_term_on_signal(int sig)
12
{
13
restore_term();
@@ -25,7 +23,7 @@ static void restore_term_on_signal(int sig)
23
static int term_fd = -1;
24
static struct termios old_term;
25
28
-static void restore_term(void)
26
+void restore_term(void)
27
{
28
if (term_fd < 0)
29
return;
@@ -35,15 +33,22 @@ static void restore_term(void)
33
term_fd = -1;
34
}
35
36
+int save_term(int full_duplex)
37
+{
38
+ if (term_fd < 0)
39
+ term_fd = open("/dev/tty", O_RDWR);
40
+
41
+ return (term_fd < 0) ? -1 : tcgetattr(term_fd, &old_term);
42
+}
43
+
44
static int disable_bits(tcflag_t bits)
45
{
46
struct termios t;
47
42
- term_fd = open("/dev/tty", O_RDWR);
43
- if (tcgetattr(term_fd, &t) < 0)
48
+ if (save_term(0) < 0)
49
goto error;
50
46
- old_term = t;
51
+ t = old_term;
52
sigchain_push_common(restore_term_on_signal);
53
54
t.c_lflag &= ~bits;
@@ -75,9 +80,10 @@ static int enable_non_canonical(void)
80
static int use_stty = 1;
81
static struct string_list stty_restore = STRING_LIST_INIT_DUP;
82
static HANDLE hconin = INVALID_HANDLE_VALUE;
78
-static DWORD cmode;
83
+static HANDLE hconout = INVALID_HANDLE_VALUE;
84
+static DWORD cmode_in, cmode_out;
85
80
-static void restore_term(void)
86
+void restore_term(void)
87
{
88
if (use_stty) {
89
int i;
@@ -97,9 +103,42 @@ static void restore_term(void)
103
if (hconin == INVALID_HANDLE_VALUE)
104
return;
105
100
- SetConsoleMode(hconin, cmode);
106
+ SetConsoleMode(hconin, cmode_in);
107
+ CloseHandle(hconin);
108
+ if (cmode_out) {
109
+ assert(hconout != INVALID_HANDLE_VALUE);
110
+ SetConsoleMode(hconout, cmode_out);
111
+ CloseHandle(hconout);
112
+ }
113
+
114
+ hconin = hconout = INVALID_HANDLE_VALUE;
115
+}
116
+
117
+int save_term(int full_duplex)
118
+{
119
+ hconin = CreateFileA("CONIN$", GENERIC_READ | GENERIC_WRITE,
120
+ FILE_SHARE_READ, NULL, OPEN_EXISTING,
121
+ FILE_ATTRIBUTE_NORMAL, NULL);
122
+ if (hconin == INVALID_HANDLE_VALUE)
123
+ return -1;
124
+
125
+ if (full_duplex) {
126
+ hconout = CreateFileA("CONOUT$", GENERIC_READ | GENERIC_WRITE,
127
+ FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
128
+ FILE_ATTRIBUTE_NORMAL, NULL);
129
+ if (hconout == INVALID_HANDLE_VALUE)
130
+ goto error;
131
+
132
+ GetConsoleMode(hconout, &cmode_out);
133
+ }
134
+
135
+ GetConsoleMode(hconin, &cmode_in);
136
+ use_stty = 0;
137
+ return 0;
138
+error:
139
CloseHandle(hconin);
140
hconin = INVALID_HANDLE_VALUE;
141
+ return -1;
142
}
143
144
static int disable_bits(DWORD bits)
@@ -135,15 +174,11 @@ static int disable_bits(DWORD bits)
174
use_stty = 0;
175
}
176
138
- hconin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
139
- FILE_SHARE_READ, NULL, OPEN_EXISTING,
140
- FILE_ATTRIBUTE_NORMAL, NULL);
141
- if (hconin == INVALID_HANDLE_VALUE)
177
+ if (save_term(0) < 0)
178
return -1;
179
144
- GetConsoleMode(hconin, &cmode);
180
sigchain_push_common(restore_term_on_signal);
146
- if (!SetConsoleMode(hconin, cmode & ~bits)) {
181
+ if (!SetConsoleMode(hconin, cmode_in & ~bits)) {
182
CloseHandle(hconin);
183
hconin = INVALID_HANDLE_VALUE;
184
return -1;
@@ -361,6 +396,16 @@ int read_key_without_echo(struct strbuf *buf)
396
397
#else
398
399
+int save_term(int full_duplex)
400
+{
401
+ /* full_duplex == 1, but no support available */
402
+ return -full_duplex;
403
+}
404
+
405
+void restore_term(void)
406
+{
407
+}
408
+
409
char *git_terminal_prompt(const char *prompt, int echo)
410
{
411
return getpass(prompt);
compat/terminal.h
+3
@@ -1,6 +1,9 @@
1
#ifndef COMPAT_TERMINAL_H
2
#define COMPAT_TERMINAL_H
3
4
+int save_term(int full_duplex);
5
+void restore_term(void);
6
+
7
char *git_terminal_prompt(const char *prompt, int echo);
8
9
/* Read a single keystroke, without echoing it to the terminal */