add generic terminal prompt function
When we need to prompt the user for input interactively, we
want to access their terminal directly. We can't rely on
stdio because it may be connected to pipes or files, rather
than the terminal. Instead, we use "getpass()", because it
abstracts the idea of prompting and reading from the
terminal. However, it has some problems:
1. It never echoes the typed characters, which makes it OK
for passwords but annoying for other input (like usernames).
2. Some implementations of getpass() have an extremely
small input buffer (e.g., Solaris 8 is reported to
support only 8 characters).
3. Some implementations of getpass() will fall back to
reading from stdin (e.g., glibc). We explicitly don't
want this, because our stdin may be connected to a pipe
speaking a particular protocol, and reading will
disrupt the protocol flow (e.g., the remote-curl
helper).
4. Some implementations of getpass() turn off signals, so
that hitting "^C" on the terminal does not break out of
the password prompt. This can be a mild annoyance.
Instead, let's provide an abstract "git_terminal_prompt"
function that addresses these concerns. This patch includes
an implementation based on /dev/tty, enabled by setting
HAVE_DEV_TTY. The fallback is to use getpass() as before.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-12-10 11:41:01 +01:00
|
|
|
#include "git-compat-util.h"
|
|
|
|
#include "compat/terminal.h"
|
|
|
|
#include "sigchain.h"
|
|
|
|
#include "strbuf.h"
|
2020-01-14 19:43:48 +01:00
|
|
|
#include "run-command.h"
|
|
|
|
#include "string-list.h"
|
add generic terminal prompt function
When we need to prompt the user for input interactively, we
want to access their terminal directly. We can't rely on
stdio because it may be connected to pipes or files, rather
than the terminal. Instead, we use "getpass()", because it
abstracts the idea of prompting and reading from the
terminal. However, it has some problems:
1. It never echoes the typed characters, which makes it OK
for passwords but annoying for other input (like usernames).
2. Some implementations of getpass() have an extremely
small input buffer (e.g., Solaris 8 is reported to
support only 8 characters).
3. Some implementations of getpass() will fall back to
reading from stdin (e.g., glibc). We explicitly don't
want this, because our stdin may be connected to a pipe
speaking a particular protocol, and reading will
disrupt the protocol flow (e.g., the remote-curl
helper).
4. Some implementations of getpass() turn off signals, so
that hitting "^C" on the terminal does not break out of
the password prompt. This can be a mild annoyance.
Instead, let's provide an abstract "git_terminal_prompt"
function that addresses these concerns. This patch includes
an implementation based on /dev/tty, enabled by setting
HAVE_DEV_TTY. The fallback is to use getpass() as before.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-12-10 11:41:01 +01:00
|
|
|
|
2013-05-02 21:26:08 +02:00
|
|
|
#if defined(HAVE_DEV_TTY) || defined(GIT_WINDOWS_NATIVE)
|
2012-12-04 09:10:41 +01:00
|
|
|
|
|
|
|
static void restore_term(void);
|
|
|
|
|
|
|
|
static void restore_term_on_signal(int sig)
|
|
|
|
{
|
|
|
|
restore_term();
|
|
|
|
sigchain_pop(sig);
|
|
|
|
raise(sig);
|
|
|
|
}
|
|
|
|
|
add generic terminal prompt function
When we need to prompt the user for input interactively, we
want to access their terminal directly. We can't rely on
stdio because it may be connected to pipes or files, rather
than the terminal. Instead, we use "getpass()", because it
abstracts the idea of prompting and reading from the
terminal. However, it has some problems:
1. It never echoes the typed characters, which makes it OK
for passwords but annoying for other input (like usernames).
2. Some implementations of getpass() have an extremely
small input buffer (e.g., Solaris 8 is reported to
support only 8 characters).
3. Some implementations of getpass() will fall back to
reading from stdin (e.g., glibc). We explicitly don't
want this, because our stdin may be connected to a pipe
speaking a particular protocol, and reading will
disrupt the protocol flow (e.g., the remote-curl
helper).
4. Some implementations of getpass() turn off signals, so
that hitting "^C" on the terminal does not break out of
the password prompt. This can be a mild annoyance.
Instead, let's provide an abstract "git_terminal_prompt"
function that addresses these concerns. This patch includes
an implementation based on /dev/tty, enabled by setting
HAVE_DEV_TTY. The fallback is to use getpass() as before.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-12-10 11:41:01 +01:00
|
|
|
#ifdef HAVE_DEV_TTY
|
|
|
|
|
2012-12-04 09:10:41 +01:00
|
|
|
#define INPUT_PATH "/dev/tty"
|
|
|
|
#define OUTPUT_PATH "/dev/tty"
|
|
|
|
|
add generic terminal prompt function
When we need to prompt the user for input interactively, we
want to access their terminal directly. We can't rely on
stdio because it may be connected to pipes or files, rather
than the terminal. Instead, we use "getpass()", because it
abstracts the idea of prompting and reading from the
terminal. However, it has some problems:
1. It never echoes the typed characters, which makes it OK
for passwords but annoying for other input (like usernames).
2. Some implementations of getpass() have an extremely
small input buffer (e.g., Solaris 8 is reported to
support only 8 characters).
3. Some implementations of getpass() will fall back to
reading from stdin (e.g., glibc). We explicitly don't
want this, because our stdin may be connected to a pipe
speaking a particular protocol, and reading will
disrupt the protocol flow (e.g., the remote-curl
helper).
4. Some implementations of getpass() turn off signals, so
that hitting "^C" on the terminal does not break out of
the password prompt. This can be a mild annoyance.
Instead, let's provide an abstract "git_terminal_prompt"
function that addresses these concerns. This patch includes
an implementation based on /dev/tty, enabled by setting
HAVE_DEV_TTY. The fallback is to use getpass() as before.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-12-10 11:41:01 +01:00
|
|
|
static int term_fd = -1;
|
|
|
|
static struct termios old_term;
|
|
|
|
|
|
|
|
static void restore_term(void)
|
|
|
|
{
|
|
|
|
if (term_fd < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
tcsetattr(term_fd, TCSAFLUSH, &old_term);
|
2012-12-04 09:10:39 +01:00
|
|
|
close(term_fd);
|
add generic terminal prompt function
When we need to prompt the user for input interactively, we
want to access their terminal directly. We can't rely on
stdio because it may be connected to pipes or files, rather
than the terminal. Instead, we use "getpass()", because it
abstracts the idea of prompting and reading from the
terminal. However, it has some problems:
1. It never echoes the typed characters, which makes it OK
for passwords but annoying for other input (like usernames).
2. Some implementations of getpass() have an extremely
small input buffer (e.g., Solaris 8 is reported to
support only 8 characters).
3. Some implementations of getpass() will fall back to
reading from stdin (e.g., glibc). We explicitly don't
want this, because our stdin may be connected to a pipe
speaking a particular protocol, and reading will
disrupt the protocol flow (e.g., the remote-curl
helper).
4. Some implementations of getpass() turn off signals, so
that hitting "^C" on the terminal does not break out of
the password prompt. This can be a mild annoyance.
Instead, let's provide an abstract "git_terminal_prompt"
function that addresses these concerns. This patch includes
an implementation based on /dev/tty, enabled by setting
HAVE_DEV_TTY. The fallback is to use getpass() as before.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-12-10 11:41:01 +01:00
|
|
|
term_fd = -1;
|
|
|
|
}
|
|
|
|
|
2020-01-14 19:43:47 +01:00
|
|
|
static int disable_bits(tcflag_t bits)
|
2012-12-04 09:10:39 +01:00
|
|
|
{
|
|
|
|
struct termios t;
|
|
|
|
|
|
|
|
term_fd = open("/dev/tty", O_RDWR);
|
|
|
|
if (tcgetattr(term_fd, &t) < 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
old_term = t;
|
|
|
|
sigchain_push_common(restore_term_on_signal);
|
|
|
|
|
2020-01-14 19:43:47 +01:00
|
|
|
t.c_lflag &= ~bits;
|
2012-12-04 09:10:39 +01:00
|
|
|
if (!tcsetattr(term_fd, TCSAFLUSH, &t))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
error:
|
|
|
|
close(term_fd);
|
|
|
|
term_fd = -1;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-01-14 19:43:47 +01:00
|
|
|
static int disable_echo(void)
|
|
|
|
{
|
|
|
|
return disable_bits(ECHO);
|
|
|
|
}
|
|
|
|
|
2020-01-14 19:43:49 +01:00
|
|
|
static int enable_non_canonical(void)
|
|
|
|
{
|
|
|
|
return disable_bits(ICANON | ECHO);
|
|
|
|
}
|
|
|
|
|
2013-05-02 21:26:08 +02:00
|
|
|
#elif defined(GIT_WINDOWS_NATIVE)
|
2012-12-04 09:10:41 +01:00
|
|
|
|
|
|
|
#define INPUT_PATH "CONIN$"
|
|
|
|
#define OUTPUT_PATH "CONOUT$"
|
|
|
|
#define FORCE_TEXT "t"
|
|
|
|
|
2020-01-14 19:43:48 +01:00
|
|
|
static int use_stty = 1;
|
|
|
|
static struct string_list stty_restore = STRING_LIST_INIT_DUP;
|
2012-12-04 09:10:41 +01:00
|
|
|
static HANDLE hconin = INVALID_HANDLE_VALUE;
|
|
|
|
static DWORD cmode;
|
|
|
|
|
|
|
|
static void restore_term(void)
|
|
|
|
{
|
2020-01-14 19:43:48 +01:00
|
|
|
if (use_stty) {
|
|
|
|
int i;
|
|
|
|
struct child_process cp = CHILD_PROCESS_INIT;
|
|
|
|
|
|
|
|
if (stty_restore.nr == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
argv_array_push(&cp.args, "stty");
|
|
|
|
for (i = 0; i < stty_restore.nr; i++)
|
|
|
|
argv_array_push(&cp.args, stty_restore.items[i].string);
|
|
|
|
run_command(&cp);
|
|
|
|
string_list_clear(&stty_restore, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-12-04 09:10:41 +01:00
|
|
|
if (hconin == INVALID_HANDLE_VALUE)
|
|
|
|
return;
|
|
|
|
|
|
|
|
SetConsoleMode(hconin, cmode);
|
|
|
|
CloseHandle(hconin);
|
|
|
|
hconin = INVALID_HANDLE_VALUE;
|
|
|
|
}
|
|
|
|
|
2020-01-14 19:43:47 +01:00
|
|
|
static int disable_bits(DWORD bits)
|
2012-12-04 09:10:41 +01:00
|
|
|
{
|
2020-01-14 19:43:48 +01:00
|
|
|
if (use_stty) {
|
|
|
|
struct child_process cp = CHILD_PROCESS_INIT;
|
|
|
|
|
|
|
|
argv_array_push(&cp.args, "stty");
|
|
|
|
|
|
|
|
if (bits & ENABLE_LINE_INPUT) {
|
|
|
|
string_list_append(&stty_restore, "icanon");
|
|
|
|
argv_array_push(&cp.args, "-icanon");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bits & ENABLE_ECHO_INPUT) {
|
|
|
|
string_list_append(&stty_restore, "echo");
|
|
|
|
argv_array_push(&cp.args, "-echo");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bits & ENABLE_PROCESSED_INPUT) {
|
|
|
|
string_list_append(&stty_restore, "-ignbrk");
|
|
|
|
string_list_append(&stty_restore, "intr");
|
|
|
|
string_list_append(&stty_restore, "^c");
|
|
|
|
argv_array_push(&cp.args, "ignbrk");
|
|
|
|
argv_array_push(&cp.args, "intr");
|
|
|
|
argv_array_push(&cp.args, "");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (run_command(&cp) == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* `stty` could not be executed; access the Console directly */
|
|
|
|
use_stty = 0;
|
|
|
|
}
|
|
|
|
|
2012-12-04 09:10:41 +01:00
|
|
|
hconin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
|
|
|
|
FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
|
|
|
FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if (hconin == INVALID_HANDLE_VALUE)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
GetConsoleMode(hconin, &cmode);
|
|
|
|
sigchain_push_common(restore_term_on_signal);
|
2020-01-14 19:43:47 +01:00
|
|
|
if (!SetConsoleMode(hconin, cmode & ~bits)) {
|
2012-12-04 09:10:41 +01:00
|
|
|
CloseHandle(hconin);
|
|
|
|
hconin = INVALID_HANDLE_VALUE;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-01-14 19:43:47 +01:00
|
|
|
static int disable_echo(void)
|
|
|
|
{
|
|
|
|
return disable_bits(ENABLE_ECHO_INPUT);
|
|
|
|
}
|
|
|
|
|
2020-01-14 19:43:49 +01:00
|
|
|
static int enable_non_canonical(void)
|
|
|
|
{
|
|
|
|
return disable_bits(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);
|
|
|
|
}
|
2020-01-14 19:43:47 +01:00
|
|
|
|
built-in add -p: handle Escape sequences in interactive.singlekey mode
This recapitulates part of b5cc003253c8 (add -i: ignore terminal escape
sequences, 2011-05-17):
add -i: ignore terminal escape sequences
On the author's terminal, the up-arrow input sequence is ^[[A, and
thus fat-fingering an up-arrow into 'git checkout -p' is quite
dangerous: git-add--interactive.perl will ignore the ^[ and [
characters and happily treat A as "discard everything".
As a band-aid fix, use Term::Cap to get all terminal capabilities.
Then use the heuristic that any capability value that starts with ^[
(i.e., \e in perl) must be a key input sequence. Finally, given an
input that starts with ^[, read more characters until we have read a
full escape sequence, then return that to the caller. We use a
timeout of 0.5 seconds on the subsequent reads to avoid getting stuck
if the user actually input a lone ^[.
Since none of the currently recognized keys start with ^[, the net
result is that the sequence as a whole will be ignored and the help
displayed.
Note that we leave part for later which uses "Term::Cap to get all
terminal capabilities", for several reasons:
1. it is actually not really necessary, as the timeout of 0.5 seconds
should be plenty sufficient to catch Escape sequences,
2. it is cleaner to keep the change to special-case Escape sequences
separate from the change that reads all terminal capabilities to
speed things up, and
3. in practice, relying on the terminal capabilities is a bit overrated,
as the information could be incomplete, or plain wrong. For example,
in this developer's tmux sessions, the terminal capabilities claim
that the "cursor up" sequence is ^[M, but the actual sequence
produced by the "cursor up" key is ^[[A.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-01-14 19:43:51 +01:00
|
|
|
/*
|
|
|
|
* Override `getchar()`, as the default implementation does not use
|
|
|
|
* `ReadFile()`.
|
|
|
|
*
|
|
|
|
* This poses a problem when we want to see whether the standard
|
|
|
|
* input has more characters, as the default of Git for Windows is to start the
|
|
|
|
* Bash in a MinTTY, which uses a named pipe to emulate a pty, in which case
|
|
|
|
* our `poll()` emulation calls `PeekNamedPipe()`, which seems to require
|
|
|
|
* `ReadFile()` to be called first to work properly (it only reports 0
|
|
|
|
* available bytes, otherwise).
|
|
|
|
*
|
|
|
|
* So let's just override `getchar()` with a version backed by `ReadFile()` and
|
|
|
|
* go our merry ways from here.
|
|
|
|
*/
|
|
|
|
static int mingw_getchar(void)
|
|
|
|
{
|
|
|
|
DWORD read = 0;
|
|
|
|
unsigned char ch;
|
|
|
|
|
|
|
|
if (!ReadFile(GetStdHandle(STD_INPUT_HANDLE), &ch, 1, &read, NULL))
|
|
|
|
return EOF;
|
|
|
|
|
|
|
|
if (!read) {
|
|
|
|
error("Unexpected 0 read");
|
|
|
|
return EOF;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ch;
|
|
|
|
}
|
|
|
|
#define getchar mingw_getchar
|
|
|
|
|
2012-12-04 09:10:41 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef FORCE_TEXT
|
|
|
|
#define FORCE_TEXT
|
|
|
|
#endif
|
|
|
|
|
add generic terminal prompt function
When we need to prompt the user for input interactively, we
want to access their terminal directly. We can't rely on
stdio because it may be connected to pipes or files, rather
than the terminal. Instead, we use "getpass()", because it
abstracts the idea of prompting and reading from the
terminal. However, it has some problems:
1. It never echoes the typed characters, which makes it OK
for passwords but annoying for other input (like usernames).
2. Some implementations of getpass() have an extremely
small input buffer (e.g., Solaris 8 is reported to
support only 8 characters).
3. Some implementations of getpass() will fall back to
reading from stdin (e.g., glibc). We explicitly don't
want this, because our stdin may be connected to a pipe
speaking a particular protocol, and reading will
disrupt the protocol flow (e.g., the remote-curl
helper).
4. Some implementations of getpass() turn off signals, so
that hitting "^C" on the terminal does not break out of
the password prompt. This can be a mild annoyance.
Instead, let's provide an abstract "git_terminal_prompt"
function that addresses these concerns. This patch includes
an implementation based on /dev/tty, enabled by setting
HAVE_DEV_TTY. The fallback is to use getpass() as before.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-12-10 11:41:01 +01:00
|
|
|
char *git_terminal_prompt(const char *prompt, int echo)
|
|
|
|
{
|
|
|
|
static struct strbuf buf = STRBUF_INIT;
|
|
|
|
int r;
|
2012-12-04 09:10:40 +01:00
|
|
|
FILE *input_fh, *output_fh;
|
add generic terminal prompt function
When we need to prompt the user for input interactively, we
want to access their terminal directly. We can't rely on
stdio because it may be connected to pipes or files, rather
than the terminal. Instead, we use "getpass()", because it
abstracts the idea of prompting and reading from the
terminal. However, it has some problems:
1. It never echoes the typed characters, which makes it OK
for passwords but annoying for other input (like usernames).
2. Some implementations of getpass() have an extremely
small input buffer (e.g., Solaris 8 is reported to
support only 8 characters).
3. Some implementations of getpass() will fall back to
reading from stdin (e.g., glibc). We explicitly don't
want this, because our stdin may be connected to a pipe
speaking a particular protocol, and reading will
disrupt the protocol flow (e.g., the remote-curl
helper).
4. Some implementations of getpass() turn off signals, so
that hitting "^C" on the terminal does not break out of
the password prompt. This can be a mild annoyance.
Instead, let's provide an abstract "git_terminal_prompt"
function that addresses these concerns. This patch includes
an implementation based on /dev/tty, enabled by setting
HAVE_DEV_TTY. The fallback is to use getpass() as before.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-12-10 11:41:01 +01:00
|
|
|
|
2012-12-04 09:10:41 +01:00
|
|
|
input_fh = fopen(INPUT_PATH, "r" FORCE_TEXT);
|
2012-12-04 09:10:40 +01:00
|
|
|
if (!input_fh)
|
add generic terminal prompt function
When we need to prompt the user for input interactively, we
want to access their terminal directly. We can't rely on
stdio because it may be connected to pipes or files, rather
than the terminal. Instead, we use "getpass()", because it
abstracts the idea of prompting and reading from the
terminal. However, it has some problems:
1. It never echoes the typed characters, which makes it OK
for passwords but annoying for other input (like usernames).
2. Some implementations of getpass() have an extremely
small input buffer (e.g., Solaris 8 is reported to
support only 8 characters).
3. Some implementations of getpass() will fall back to
reading from stdin (e.g., glibc). We explicitly don't
want this, because our stdin may be connected to a pipe
speaking a particular protocol, and reading will
disrupt the protocol flow (e.g., the remote-curl
helper).
4. Some implementations of getpass() turn off signals, so
that hitting "^C" on the terminal does not break out of
the password prompt. This can be a mild annoyance.
Instead, let's provide an abstract "git_terminal_prompt"
function that addresses these concerns. This patch includes
an implementation based on /dev/tty, enabled by setting
HAVE_DEV_TTY. The fallback is to use getpass() as before.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-12-10 11:41:01 +01:00
|
|
|
return NULL;
|
|
|
|
|
2012-12-04 09:10:41 +01:00
|
|
|
output_fh = fopen(OUTPUT_PATH, "w" FORCE_TEXT);
|
2012-12-04 09:10:40 +01:00
|
|
|
if (!output_fh) {
|
|
|
|
fclose(input_fh);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-12-04 09:10:39 +01:00
|
|
|
if (!echo && disable_echo()) {
|
2012-12-04 09:10:40 +01:00
|
|
|
fclose(input_fh);
|
|
|
|
fclose(output_fh);
|
2012-12-04 09:10:39 +01:00
|
|
|
return NULL;
|
add generic terminal prompt function
When we need to prompt the user for input interactively, we
want to access their terminal directly. We can't rely on
stdio because it may be connected to pipes or files, rather
than the terminal. Instead, we use "getpass()", because it
abstracts the idea of prompting and reading from the
terminal. However, it has some problems:
1. It never echoes the typed characters, which makes it OK
for passwords but annoying for other input (like usernames).
2. Some implementations of getpass() have an extremely
small input buffer (e.g., Solaris 8 is reported to
support only 8 characters).
3. Some implementations of getpass() will fall back to
reading from stdin (e.g., glibc). We explicitly don't
want this, because our stdin may be connected to a pipe
speaking a particular protocol, and reading will
disrupt the protocol flow (e.g., the remote-curl
helper).
4. Some implementations of getpass() turn off signals, so
that hitting "^C" on the terminal does not break out of
the password prompt. This can be a mild annoyance.
Instead, let's provide an abstract "git_terminal_prompt"
function that addresses these concerns. This patch includes
an implementation based on /dev/tty, enabled by setting
HAVE_DEV_TTY. The fallback is to use getpass() as before.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-12-10 11:41:01 +01:00
|
|
|
}
|
|
|
|
|
2012-12-04 09:10:40 +01:00
|
|
|
fputs(prompt, output_fh);
|
|
|
|
fflush(output_fh);
|
add generic terminal prompt function
When we need to prompt the user for input interactively, we
want to access their terminal directly. We can't rely on
stdio because it may be connected to pipes or files, rather
than the terminal. Instead, we use "getpass()", because it
abstracts the idea of prompting and reading from the
terminal. However, it has some problems:
1. It never echoes the typed characters, which makes it OK
for passwords but annoying for other input (like usernames).
2. Some implementations of getpass() have an extremely
small input buffer (e.g., Solaris 8 is reported to
support only 8 characters).
3. Some implementations of getpass() will fall back to
reading from stdin (e.g., glibc). We explicitly don't
want this, because our stdin may be connected to a pipe
speaking a particular protocol, and reading will
disrupt the protocol flow (e.g., the remote-curl
helper).
4. Some implementations of getpass() turn off signals, so
that hitting "^C" on the terminal does not break out of
the password prompt. This can be a mild annoyance.
Instead, let's provide an abstract "git_terminal_prompt"
function that addresses these concerns. This patch includes
an implementation based on /dev/tty, enabled by setting
HAVE_DEV_TTY. The fallback is to use getpass() as before.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-12-10 11:41:01 +01:00
|
|
|
|
2016-01-14 00:31:17 +01:00
|
|
|
r = strbuf_getline_lf(&buf, input_fh);
|
add generic terminal prompt function
When we need to prompt the user for input interactively, we
want to access their terminal directly. We can't rely on
stdio because it may be connected to pipes or files, rather
than the terminal. Instead, we use "getpass()", because it
abstracts the idea of prompting and reading from the
terminal. However, it has some problems:
1. It never echoes the typed characters, which makes it OK
for passwords but annoying for other input (like usernames).
2. Some implementations of getpass() have an extremely
small input buffer (e.g., Solaris 8 is reported to
support only 8 characters).
3. Some implementations of getpass() will fall back to
reading from stdin (e.g., glibc). We explicitly don't
want this, because our stdin may be connected to a pipe
speaking a particular protocol, and reading will
disrupt the protocol flow (e.g., the remote-curl
helper).
4. Some implementations of getpass() turn off signals, so
that hitting "^C" on the terminal does not break out of
the password prompt. This can be a mild annoyance.
Instead, let's provide an abstract "git_terminal_prompt"
function that addresses these concerns. This patch includes
an implementation based on /dev/tty, enabled by setting
HAVE_DEV_TTY. The fallback is to use getpass() as before.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-12-10 11:41:01 +01:00
|
|
|
if (!echo) {
|
2012-12-04 09:10:40 +01:00
|
|
|
putc('\n', output_fh);
|
|
|
|
fflush(output_fh);
|
add generic terminal prompt function
When we need to prompt the user for input interactively, we
want to access their terminal directly. We can't rely on
stdio because it may be connected to pipes or files, rather
than the terminal. Instead, we use "getpass()", because it
abstracts the idea of prompting and reading from the
terminal. However, it has some problems:
1. It never echoes the typed characters, which makes it OK
for passwords but annoying for other input (like usernames).
2. Some implementations of getpass() have an extremely
small input buffer (e.g., Solaris 8 is reported to
support only 8 characters).
3. Some implementations of getpass() will fall back to
reading from stdin (e.g., glibc). We explicitly don't
want this, because our stdin may be connected to a pipe
speaking a particular protocol, and reading will
disrupt the protocol flow (e.g., the remote-curl
helper).
4. Some implementations of getpass() turn off signals, so
that hitting "^C" on the terminal does not break out of
the password prompt. This can be a mild annoyance.
Instead, let's provide an abstract "git_terminal_prompt"
function that addresses these concerns. This patch includes
an implementation based on /dev/tty, enabled by setting
HAVE_DEV_TTY. The fallback is to use getpass() as before.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-12-10 11:41:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
restore_term();
|
2012-12-04 09:10:40 +01:00
|
|
|
fclose(input_fh);
|
|
|
|
fclose(output_fh);
|
add generic terminal prompt function
When we need to prompt the user for input interactively, we
want to access their terminal directly. We can't rely on
stdio because it may be connected to pipes or files, rather
than the terminal. Instead, we use "getpass()", because it
abstracts the idea of prompting and reading from the
terminal. However, it has some problems:
1. It never echoes the typed characters, which makes it OK
for passwords but annoying for other input (like usernames).
2. Some implementations of getpass() have an extremely
small input buffer (e.g., Solaris 8 is reported to
support only 8 characters).
3. Some implementations of getpass() will fall back to
reading from stdin (e.g., glibc). We explicitly don't
want this, because our stdin may be connected to a pipe
speaking a particular protocol, and reading will
disrupt the protocol flow (e.g., the remote-curl
helper).
4. Some implementations of getpass() turn off signals, so
that hitting "^C" on the terminal does not break out of
the password prompt. This can be a mild annoyance.
Instead, let's provide an abstract "git_terminal_prompt"
function that addresses these concerns. This patch includes
an implementation based on /dev/tty, enabled by setting
HAVE_DEV_TTY. The fallback is to use getpass() as before.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-12-10 11:41:01 +01:00
|
|
|
|
|
|
|
if (r == EOF)
|
|
|
|
return NULL;
|
|
|
|
return buf.buf;
|
|
|
|
}
|
|
|
|
|
2020-01-14 19:43:49 +01:00
|
|
|
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);
|
built-in add -p: handle Escape sequences in interactive.singlekey mode
This recapitulates part of b5cc003253c8 (add -i: ignore terminal escape
sequences, 2011-05-17):
add -i: ignore terminal escape sequences
On the author's terminal, the up-arrow input sequence is ^[[A, and
thus fat-fingering an up-arrow into 'git checkout -p' is quite
dangerous: git-add--interactive.perl will ignore the ^[ and [
characters and happily treat A as "discard everything".
As a band-aid fix, use Term::Cap to get all terminal capabilities.
Then use the heuristic that any capability value that starts with ^[
(i.e., \e in perl) must be a key input sequence. Finally, given an
input that starts with ^[, read more characters until we have read a
full escape sequence, then return that to the caller. We use a
timeout of 0.5 seconds on the subsequent reads to avoid getting stuck
if the user actually input a lone ^[.
Since none of the currently recognized keys start with ^[, the net
result is that the sequence as a whole will be ignored and the help
displayed.
Note that we leave part for later which uses "Term::Cap to get all
terminal capabilities", for several reasons:
1. it is actually not really necessary, as the timeout of 0.5 seconds
should be plenty sufficient to catch Escape sequences,
2. it is cleaner to keep the change to special-case Escape sequences
separate from the change that reads all terminal capabilities to
speed things up, and
3. in practice, relying on the terminal capabilities is a bit overrated,
as the information could be incomplete, or plain wrong. For example,
in this developer's tmux sessions, the terminal capabilities claim
that the "cursor up" sequence is ^[M, but the actual sequence
produced by the "cursor up" key is ^[[A.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-01-14 19:43:51 +01:00
|
|
|
|
|
|
|
if (ch == '\033' /* ESC */) {
|
|
|
|
/*
|
|
|
|
* We are most likely looking at an Escape sequence. Let's try
|
|
|
|
* to read more bytes, waiting at most half a second, assuming
|
|
|
|
* that the sequence is complete if we did not receive any byte
|
|
|
|
* within that time.
|
|
|
|
*
|
|
|
|
* Start by replacing the Escape byte with ^[ */
|
|
|
|
strbuf_splice(buf, buf->len - 1, 1, "^[", 2);
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
struct pollfd pfd = { .fd = 0, .events = POLLIN };
|
|
|
|
|
|
|
|
if (poll(&pfd, 1, 500) < 1)
|
|
|
|
break;
|
|
|
|
|
|
|
|
ch = getchar();
|
|
|
|
if (ch == EOF)
|
|
|
|
return 0;
|
|
|
|
strbuf_addch(buf, ch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-14 19:43:49 +01:00
|
|
|
restore_term();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
add generic terminal prompt function
When we need to prompt the user for input interactively, we
want to access their terminal directly. We can't rely on
stdio because it may be connected to pipes or files, rather
than the terminal. Instead, we use "getpass()", because it
abstracts the idea of prompting and reading from the
terminal. However, it has some problems:
1. It never echoes the typed characters, which makes it OK
for passwords but annoying for other input (like usernames).
2. Some implementations of getpass() have an extremely
small input buffer (e.g., Solaris 8 is reported to
support only 8 characters).
3. Some implementations of getpass() will fall back to
reading from stdin (e.g., glibc). We explicitly don't
want this, because our stdin may be connected to a pipe
speaking a particular protocol, and reading will
disrupt the protocol flow (e.g., the remote-curl
helper).
4. Some implementations of getpass() turn off signals, so
that hitting "^C" on the terminal does not break out of
the password prompt. This can be a mild annoyance.
Instead, let's provide an abstract "git_terminal_prompt"
function that addresses these concerns. This patch includes
an implementation based on /dev/tty, enabled by setting
HAVE_DEV_TTY. The fallback is to use getpass() as before.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-12-10 11:41:01 +01:00
|
|
|
#else
|
|
|
|
|
|
|
|
char *git_terminal_prompt(const char *prompt, int echo)
|
|
|
|
{
|
|
|
|
return getpass(prompt);
|
|
|
|
}
|
|
|
|
|
2020-01-14 19:43:49 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
add generic terminal prompt function
When we need to prompt the user for input interactively, we
want to access their terminal directly. We can't rely on
stdio because it may be connected to pipes or files, rather
than the terminal. Instead, we use "getpass()", because it
abstracts the idea of prompting and reading from the
terminal. However, it has some problems:
1. It never echoes the typed characters, which makes it OK
for passwords but annoying for other input (like usernames).
2. Some implementations of getpass() have an extremely
small input buffer (e.g., Solaris 8 is reported to
support only 8 characters).
3. Some implementations of getpass() will fall back to
reading from stdin (e.g., glibc). We explicitly don't
want this, because our stdin may be connected to a pipe
speaking a particular protocol, and reading will
disrupt the protocol flow (e.g., the remote-curl
helper).
4. Some implementations of getpass() turn off signals, so
that hitting "^C" on the terminal does not break out of
the password prompt. This can be a mild annoyance.
Instead, let's provide an abstract "git_terminal_prompt"
function that addresses these concerns. This patch includes
an implementation based on /dev/tty, enabled by setting
HAVE_DEV_TTY. The fallback is to use getpass() as before.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-12-10 11:41:01 +01:00
|
|
|
#endif
|