summaryrefslogtreecommitdiff
path: root/compat
diff options
context:
space:
mode:
authorJohannes Schindelin <johannes.schindelin@gmx.de>2020-01-14 18:43:49 (GMT)
committerJunio C Hamano <gitster@pobox.com>2020-01-15 20:06:17 (GMT)
commita5e46e6b0101b960c131dd39b50999cc0e69ed2b (patch)
tree015f926507d78291928b4e6c32c65346b1d98f14 /compat
parent9ea416cb511e87f830462b08ae74b8a78fcca223 (diff)
downloadgit-a5e46e6b0101b960c131dd39b50999cc0e69ed2b.zip
git-a5e46e6b0101b960c131dd39b50999cc0e69ed2b.tar.gz
git-a5e46e6b0101b960c131dd39b50999cc0e69ed2b.tar.bz2
terminal: add a new function to read a single keystroke
Typically, input on the command-line is line-based. It is actually not really easy to get single characters (or better put: keystrokes). We provide two implementations here: - One that handles `/dev/tty` based systems as well as native Windows. The former uses the `tcsetattr()` function to put the terminal into "raw mode", which allows us to read individual keystrokes, one by one. The latter uses `stty.exe` to do the same, falling back to direct Win32 Console access. Thanks to the refactoring leading up to this commit, this is a single function, with the platform-specific details hidden away in conditionally-compiled code blocks. - A fall-back which simply punts and reads back an entire line. Note that the function writes the keystroke into an `strbuf` rather than a `char`, in preparation for reading Escape sequences (e.g. when the user hit an arrow key). This is also required for UTF-8 sequences in case the keystroke corresponds to a non-ASCII letter. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'compat')
-rw-r--r--compat/terminal.c55
-rw-r--r--compat/terminal.h3
2 files changed, 58 insertions, 0 deletions
diff --git a/compat/terminal.c b/compat/terminal.c
index 16e9949..1b25640 100644
--- a/compat/terminal.c
+++ b/compat/terminal.c
@@ -60,6 +60,11 @@ static int disable_echo(void)
return disable_bits(ECHO);
}
+static int enable_non_canonical(void)
+{
+ return disable_bits(ICANON | ECHO);
+}
+
#elif defined(GIT_WINDOWS_NATIVE)
#define INPUT_PATH "CONIN$"
@@ -151,6 +156,10 @@ static int disable_echo(void)
return disable_bits(ENABLE_ECHO_INPUT);
}
+static int enable_non_canonical(void)
+{
+ return disable_bits(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);
+}
#endif
@@ -198,6 +207,33 @@ char *git_terminal_prompt(const char *prompt, int echo)
return buf.buf;
}
+int read_key_without_echo(struct strbuf *buf)
+{
+ static int warning_displayed;
+ int ch;
+
+ if (warning_displayed || enable_non_canonical() < 0) {
+ if (!warning_displayed) {
+ warning("reading single keystrokes not supported on "
+ "this platform; reading line instead");
+ warning_displayed = 1;
+ }
+
+ return strbuf_getline(buf, stdin);
+ }
+
+ strbuf_reset(buf);
+ ch = getchar();
+ if (ch == EOF) {
+ restore_term();
+ return EOF;
+ }
+
+ strbuf_addch(buf, ch);
+ restore_term();
+ return 0;
+}
+
#else
char *git_terminal_prompt(const char *prompt, int echo)
@@ -205,4 +241,23 @@ char *git_terminal_prompt(const char *prompt, int echo)
return getpass(prompt);
}
+int read_key_without_echo(struct strbuf *buf)
+{
+ static int warning_displayed;
+ const char *res;
+
+ if (!warning_displayed) {
+ warning("reading single keystrokes not supported on this "
+ "platform; reading line instead");
+ warning_displayed = 1;
+ }
+
+ res = getpass("");
+ strbuf_reset(buf);
+ if (!res)
+ return EOF;
+ strbuf_addstr(buf, res);
+ return 0;
+}
+
#endif
diff --git a/compat/terminal.h b/compat/terminal.h
index 97db7cd..a9d52b8 100644
--- a/compat/terminal.h
+++ b/compat/terminal.h
@@ -3,4 +3,7 @@
char *git_terminal_prompt(const char *prompt, int echo);
+/* Read a single keystroke, without echoing it to the terminal */
+int read_key_without_echo(struct strbuf *buf);
+
#endif /* COMPAT_TERMINAL_H */