Git fork

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

+71 -15
+60 -15
compat/terminal.c
··· 8 8 9 9 #if defined(HAVE_DEV_TTY) || defined(GIT_WINDOWS_NATIVE) 10 10 11 - static void restore_term(void); 12 - 13 11 static void restore_term_on_signal(int sig) 14 12 { 15 13 restore_term(); ··· 25 23 static int term_fd = -1; 26 24 static struct termios old_term; 27 25 28 - static void restore_term(void) 26 + void restore_term(void) 29 27 { 30 28 if (term_fd < 0) 31 29 return; ··· 35 33 term_fd = -1; 36 34 } 37 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 + 38 44 static int disable_bits(tcflag_t bits) 39 45 { 40 46 struct termios t; 41 47 42 - term_fd = open("/dev/tty", O_RDWR); 43 - if (tcgetattr(term_fd, &t) < 0) 48 + if (save_term(0) < 0) 44 49 goto error; 45 50 46 - old_term = t; 51 + t = old_term; 47 52 sigchain_push_common(restore_term_on_signal); 48 53 49 54 t.c_lflag &= ~bits; ··· 75 80 static int use_stty = 1; 76 81 static struct string_list stty_restore = STRING_LIST_INIT_DUP; 77 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; 79 85 80 - static void restore_term(void) 86 + void restore_term(void) 81 87 { 82 88 if (use_stty) { 83 89 int i; ··· 97 103 if (hconin == INVALID_HANDLE_VALUE) 98 104 return; 99 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: 101 139 CloseHandle(hconin); 102 140 hconin = INVALID_HANDLE_VALUE; 141 + return -1; 103 142 } 104 143 105 144 static int disable_bits(DWORD bits) ··· 135 174 use_stty = 0; 136 175 } 137 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) 142 178 return -1; 143 179 144 - GetConsoleMode(hconin, &cmode); 145 180 sigchain_push_common(restore_term_on_signal); 146 - if (!SetConsoleMode(hconin, cmode & ~bits)) { 181 + if (!SetConsoleMode(hconin, cmode_in & ~bits)) { 147 182 CloseHandle(hconin); 148 183 hconin = INVALID_HANDLE_VALUE; 149 184 return -1; ··· 360 395 } 361 396 362 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 + } 363 408 364 409 char *git_terminal_prompt(const char *prompt, int echo) 365 410 {
+3
compat/terminal.h
··· 1 1 #ifndef COMPAT_TERMINAL_H 2 2 #define COMPAT_TERMINAL_H 3 3 4 + int save_term(int full_duplex); 5 + void restore_term(void); 6 + 4 7 char *git_terminal_prompt(const char *prompt, int echo); 5 8 6 9 /* Read a single keystroke, without echoing it to the terminal */
+8
editor.c
··· 3 3 #include "strbuf.h" 4 4 #include "run-command.h" 5 5 #include "sigchain.h" 6 + #include "compat/terminal.h" 6 7 7 8 #ifndef DEFAULT_EDITOR 8 9 #define DEFAULT_EDITOR "vi" ··· 50 51 static int launch_specified_editor(const char *editor, const char *path, 51 52 struct strbuf *buffer, const char *const *env) 52 53 { 54 + int term_fail; 55 + 53 56 if (!editor) 54 57 return error("Terminal is dumb, but EDITOR unset"); 55 58 ··· 83 86 p.env = env; 84 87 p.use_shell = 1; 85 88 p.trace2_child_class = "editor"; 89 + term_fail = save_term(1); 86 90 if (start_command(&p) < 0) { 91 + if (!term_fail) 92 + restore_term(); 87 93 strbuf_release(&realpath); 88 94 return error("unable to start editor '%s'", editor); 89 95 } ··· 91 97 sigchain_push(SIGINT, SIG_IGN); 92 98 sigchain_push(SIGQUIT, SIG_IGN); 93 99 ret = finish_command(&p); 100 + if (!term_fail) 101 + restore_term(); 94 102 strbuf_release(&realpath); 95 103 sig = ret - 128; 96 104 sigchain_pop(SIGINT);