diff mbox series

[RFC,1/2] terminal: teach git how to save/restore its terminal settings

Message ID 20211002153654.52443-2-carenas@gmail.com (mailing list archive)
State Superseded
Headers show
Series protect git from a rogue editor | expand

Commit Message

Carlo Marcelo Arenas Belón Oct. 2, 2021, 3:36 p.m. UTC
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 <carenas@gmail.com>
---
 compat/terminal.c | 72 ++++++++++++++++++++++++++++++++++++++---------
 compat/terminal.h |  3 ++
 2 files changed, 62 insertions(+), 13 deletions(-)
diff mbox series

Patch

diff --git a/compat/terminal.c b/compat/terminal.c
index 43b73ddc75..d3f858fe94 100644
--- a/compat/terminal.c
+++ b/compat/terminal.c
@@ -25,25 +25,38 @@  static void restore_term_on_signal(int sig)
 static int term_fd = -1;
 static struct termios old_term;
 
-static void restore_term(void)
+void pop_term(void)
 {
 	if (term_fd < 0)
 		return;
 
 	tcsetattr(term_fd, TCSAFLUSH, &old_term);
+}
+
+static void restore_term(void)
+{
+	pop_term();
+
 	close(term_fd);
 	term_fd = -1;
 }
 
+int push_term(int full_duplex)
+{
+	if (term_fd < 0)
+		term_fd = open("/dev/tty", O_RDWR);
+
+	return (term_fd < 0) ? -1 : tcgetattr(term_fd, &old_term);
+}
+
 static int disable_bits(tcflag_t bits)
 {
 	struct termios t;
 
-	term_fd = open("/dev/tty", O_RDWR);
-	if (tcgetattr(term_fd, &t) < 0)
+	if (push_term(0) < 0)
 		goto error;
 
-	old_term = t;
+	t = old_term;
 	sigchain_push_common(restore_term_on_signal);
 
 	t.c_lflag &= ~bits;
@@ -75,7 +88,22 @@  static int enable_non_canonical(void)
 static int use_stty = 1;
 static struct string_list stty_restore = STRING_LIST_INIT_DUP;
 static HANDLE hconin = INVALID_HANDLE_VALUE;
-static DWORD cmode;
+static HANDLE hconout = INVALID_HANDLE_VALUE;
+static DWORD cmode_in, cmode_out;
+
+void pop_term(void)
+{
+	if (hconin == INVALID_HANDLE_VALUE)
+		return;
+
+	SetConsoleMode(hconin, cmode_in);
+	CloseHandle(hconin);
+	if (cmodeout) {
+		assert(hconout != INVALID_HANDLE_VALUE);
+		SetConsoleMode(hconout, cmode_out);
+		CloseHandle(hconout);
+	}
+}
 
 static void restore_term(void)
 {
@@ -94,12 +122,34 @@  static void restore_term(void)
 		return;
 	}
 
+	pop_term();
+	hconin = hconout = INVALID_HANDLE_VALUE;
+}
+
+int push_term(int full_duplex)
+{
+	hconin = CreateFileA("CONIN$", GENERIC_READ | GENERIC_WRITE,
+	    FILE_SHARE_READ, NULL, OPEN_EXISTING,
+	    FILE_ATTRIBUTE_NORMAL, NULL);
 	if (hconin == INVALID_HANDLE_VALUE)
-		return;
+		return -1;
+
+	if (full_duplex) {
+		hconout = CreateFileA("CONOUT$", GENERIC_READ | GENERIC_WRITE,
+			FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+			FILE_ATTRIBUTE_NORMAL, NULL);
+		if (hconout == INVALID_HANDLE_VALUE)
+			goto error;
 
-	SetConsoleMode(hconin, cmode);
+		GetConsoleMode(hconout, &cmode_out);
+	}
+
+	GetConsoleMode(hconin, &cmode_in);
+	return 0;
+error:
 	CloseHandle(hconin);
 	hconin = INVALID_HANDLE_VALUE;
+	return -1;
 }
 
 static int disable_bits(DWORD bits)
@@ -135,15 +185,11 @@  static int disable_bits(DWORD bits)
 		use_stty = 0;
 	}
 
-	hconin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
-	    FILE_SHARE_READ, NULL, OPEN_EXISTING,
-	    FILE_ATTRIBUTE_NORMAL, NULL);
-	if (hconin == INVALID_HANDLE_VALUE)
+	if (push_term(0) < 0)
 		return -1;
 
-	GetConsoleMode(hconin, &cmode);
 	sigchain_push_common(restore_term_on_signal);
-	if (!SetConsoleMode(hconin, cmode & ~bits)) {
+	if (!SetConsoleMode(hconin, cmode_in & ~bits)) {
 		CloseHandle(hconin);
 		hconin = INVALID_HANDLE_VALUE;
 		return -1;
diff --git a/compat/terminal.h b/compat/terminal.h
index a9d52b8464..5cc19116b7 100644
--- a/compat/terminal.h
+++ b/compat/terminal.h
@@ -1,6 +1,9 @@ 
 #ifndef COMPAT_TERMINAL_H
 #define COMPAT_TERMINAL_H
 
+int push_term(int full_duplex);
+void pop_term(void);
+
 char *git_terminal_prompt(const char *prompt, int echo);
 
 /* Read a single keystroke, without echoing it to the terminal */