Skip to content

Commit 853ec9a

Browse files
committed
Merge branch 'cm/save-restore-terminal'
An editor session launched during a Git operation (e.g. during 'git commit') can leave the terminal in a funny state. The code path has updated to save the terminal state before, and restore it after, it spawns an editor. * cm/save-restore-terminal: editor: save and reset terminal after calling EDITOR terminal: teach git how to save/restore its terminal settings
2 parents a4b9fb6 + 3d411af commit 853ec9a

File tree

3 files changed

+71
-15
lines changed

3 files changed

+71
-15
lines changed

compat/terminal.c

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88

99
#if defined(HAVE_DEV_TTY) || defined(GIT_WINDOWS_NATIVE)
1010

11-
static void restore_term(void);
12-
1311
static void restore_term_on_signal(int sig)
1412
{
1513
restore_term();
@@ -25,7 +23,7 @@ static void restore_term_on_signal(int sig)
2523
static int term_fd = -1;
2624
static struct termios old_term;
2725

28-
static void restore_term(void)
26+
void restore_term(void)
2927
{
3028
if (term_fd < 0)
3129
return;
@@ -35,15 +33,22 @@ static void restore_term(void)
3533
term_fd = -1;
3634
}
3735

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+
3844
static int disable_bits(tcflag_t bits)
3945
{
4046
struct termios t;
4147

42-
term_fd = open("/dev/tty", O_RDWR);
43-
if (tcgetattr(term_fd, &t) < 0)
48+
if (save_term(0) < 0)
4449
goto error;
4550

46-
old_term = t;
51+
t = old_term;
4752
sigchain_push_common(restore_term_on_signal);
4853

4954
t.c_lflag &= ~bits;
@@ -75,9 +80,10 @@ static int enable_non_canonical(void)
7580
static int use_stty = 1;
7681
static struct string_list stty_restore = STRING_LIST_INIT_DUP;
7782
static HANDLE hconin = INVALID_HANDLE_VALUE;
78-
static DWORD cmode;
83+
static HANDLE hconout = INVALID_HANDLE_VALUE;
84+
static DWORD cmode_in, cmode_out;
7985

80-
static void restore_term(void)
86+
void restore_term(void)
8187
{
8288
if (use_stty) {
8389
int i;
@@ -97,9 +103,42 @@ static void restore_term(void)
97103
if (hconin == INVALID_HANDLE_VALUE)
98104
return;
99105

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:
101139
CloseHandle(hconin);
102140
hconin = INVALID_HANDLE_VALUE;
141+
return -1;
103142
}
104143

105144
static int disable_bits(DWORD bits)
@@ -135,15 +174,11 @@ static int disable_bits(DWORD bits)
135174
use_stty = 0;
136175
}
137176

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)
142178
return -1;
143179

144-
GetConsoleMode(hconin, &cmode);
145180
sigchain_push_common(restore_term_on_signal);
146-
if (!SetConsoleMode(hconin, cmode & ~bits)) {
181+
if (!SetConsoleMode(hconin, cmode_in & ~bits)) {
147182
CloseHandle(hconin);
148183
hconin = INVALID_HANDLE_VALUE;
149184
return -1;
@@ -361,6 +396,16 @@ int read_key_without_echo(struct strbuf *buf)
361396

362397
#else
363398

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+
364409
char *git_terminal_prompt(const char *prompt, int echo)
365410
{
366411
return getpass(prompt);

compat/terminal.h

Lines changed: 3 additions & 0 deletions
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 save_term(int full_duplex);
5+
void restore_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 */

editor.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "strbuf.h"
44
#include "run-command.h"
55
#include "sigchain.h"
6+
#include "compat/terminal.h"
67

78
#ifndef DEFAULT_EDITOR
89
#define DEFAULT_EDITOR "vi"
@@ -50,6 +51,8 @@ const char *git_sequence_editor(void)
5051
static int launch_specified_editor(const char *editor, const char *path,
5152
struct strbuf *buffer, const char *const *env)
5253
{
54+
int term_fail;
55+
5356
if (!editor)
5457
return error("Terminal is dumb, but EDITOR unset");
5558

@@ -83,14 +86,19 @@ static int launch_specified_editor(const char *editor, const char *path,
8386
p.env = env;
8487
p.use_shell = 1;
8588
p.trace2_child_class = "editor";
89+
term_fail = save_term(1);
8690
if (start_command(&p) < 0) {
91+
if (!term_fail)
92+
restore_term();
8793
strbuf_release(&realpath);
8894
return error("unable to start editor '%s'", editor);
8995
}
9096

9197
sigchain_push(SIGINT, SIG_IGN);
9298
sigchain_push(SIGQUIT, SIG_IGN);
9399
ret = finish_command(&p);
100+
if (!term_fail)
101+
restore_term();
94102
strbuf_release(&realpath);
95103
sig = ret - 128;
96104
sigchain_pop(SIGINT);

0 commit comments

Comments
 (0)