Skip to content

Commit 755412f

Browse files
committed
terminal: teach git how to save/restore its terminal settings
Add two functions to push/pop the terminal settings using either the POSIX or Windows console functions, and refactor the current code to use them. This will allow for the state of the terminal to be saved and restored around a child that might need to change them (ex vi) and might not restore them properly upon exit. Signed-off-by: Carlo Marcelo Arenas Belón <[email protected]>
1 parent 225bc32 commit 755412f

File tree

2 files changed

+62
-13
lines changed

2 files changed

+62
-13
lines changed

Diff for: compat/terminal.c

+59-13
Original file line numberDiff line numberDiff line change
@@ -25,25 +25,38 @@ static void restore_term_on_signal(int sig)
2525
static int term_fd = -1;
2626
static struct termios old_term;
2727

28-
static void restore_term(void)
28+
void pop_term(void)
2929
{
3030
if (term_fd < 0)
3131
return;
3232

3333
tcsetattr(term_fd, TCSAFLUSH, &old_term);
34+
}
35+
36+
static void restore_term(void)
37+
{
38+
pop_term();
39+
3440
close(term_fd);
3541
term_fd = -1;
3642
}
3743

44+
int push_term(int full_duplex)
45+
{
46+
if (term_fd < 0)
47+
term_fd = open("/dev/tty", O_RDWR);
48+
49+
return (term_fd < 0) ? -1 : tcgetattr(term_fd, &old_term);
50+
}
51+
3852
static int disable_bits(tcflag_t bits)
3953
{
4054
struct termios t;
4155

42-
term_fd = open("/dev/tty", O_RDWR);
43-
if (tcgetattr(term_fd, &t) < 0)
56+
if (push_term(0) < 0)
4457
goto error;
4558

46-
old_term = t;
59+
t = old_term;
4760
sigchain_push_common(restore_term_on_signal);
4861

4962
t.c_lflag &= ~bits;
@@ -75,7 +88,22 @@ static int enable_non_canonical(void)
7588
static int use_stty = 1;
7689
static struct string_list stty_restore = STRING_LIST_INIT_DUP;
7790
static HANDLE hconin = INVALID_HANDLE_VALUE;
78-
static DWORD cmode;
91+
static HANDLE hconout = INVALID_HANDLE_VALUE;
92+
static DWORD cmode_in, cmode_out;
93+
94+
void pop_term(void)
95+
{
96+
if (hconin == INVALID_HANDLE_VALUE)
97+
return;
98+
99+
SetConsoleMode(hconin, cmode_in);
100+
CloseHandle(hconin);
101+
if (cmode_out) {
102+
assert(hconout != INVALID_HANDLE_VALUE);
103+
SetConsoleMode(hconout, cmode_out);
104+
CloseHandle(hconout);
105+
}
106+
}
79107

80108
static void restore_term(void)
81109
{
@@ -94,12 +122,34 @@ static void restore_term(void)
94122
return;
95123
}
96124

125+
pop_term();
126+
hconin = hconout = INVALID_HANDLE_VALUE;
127+
}
128+
129+
int push_term(int full_duplex)
130+
{
131+
hconin = CreateFileA("CONIN$", GENERIC_READ | GENERIC_WRITE,
132+
FILE_SHARE_READ, NULL, OPEN_EXISTING,
133+
FILE_ATTRIBUTE_NORMAL, NULL);
97134
if (hconin == INVALID_HANDLE_VALUE)
98-
return;
135+
return -1;
136+
137+
if (full_duplex) {
138+
hconout = CreateFileA("CONOUT$", GENERIC_READ | GENERIC_WRITE,
139+
FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
140+
FILE_ATTRIBUTE_NORMAL, NULL);
141+
if (hconout == INVALID_HANDLE_VALUE)
142+
goto error;
99143

100-
SetConsoleMode(hconin, cmode);
144+
GetConsoleMode(hconout, &cmode_out);
145+
}
146+
147+
GetConsoleMode(hconin, &cmode_in);
148+
return 0;
149+
error:
101150
CloseHandle(hconin);
102151
hconin = INVALID_HANDLE_VALUE;
152+
return -1;
103153
}
104154

105155
static int disable_bits(DWORD bits)
@@ -135,15 +185,11 @@ static int disable_bits(DWORD bits)
135185
use_stty = 0;
136186
}
137187

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)
188+
if (push_term(0) < 0)
142189
return -1;
143190

144-
GetConsoleMode(hconin, &cmode);
145191
sigchain_push_common(restore_term_on_signal);
146-
if (!SetConsoleMode(hconin, cmode & ~bits)) {
192+
if (!SetConsoleMode(hconin, cmode_in & ~bits)) {
147193
CloseHandle(hconin);
148194
hconin = INVALID_HANDLE_VALUE;
149195
return -1;

Diff for: compat/terminal.h

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
#ifndef COMPAT_TERMINAL_H
22
#define COMPAT_TERMINAL_H
33

4+
int push_term(int full_duplex);
5+
void pop_term(void);
6+
47
char *git_terminal_prompt(const char *prompt, int echo);
58

69
/* Read a single keystroke, without echoing it to the terminal */

0 commit comments

Comments
 (0)