diff options
Diffstat (limited to 'compat/terminal.c')
-rw-r--r-- | compat/terminal.c | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/compat/terminal.c b/compat/terminal.c new file mode 100644 index 0000000000..313897d581 --- /dev/null +++ b/compat/terminal.c @@ -0,0 +1,147 @@ +#include "git-compat-util.h" +#include "compat/terminal.h" +#include "sigchain.h" +#include "strbuf.h" + +#if defined(HAVE_DEV_TTY) || defined(GIT_WINDOWS_NATIVE) + +static void restore_term(void); + +static void restore_term_on_signal(int sig) +{ + restore_term(); + sigchain_pop(sig); + raise(sig); +} + +#ifdef HAVE_DEV_TTY + +#define INPUT_PATH "/dev/tty" +#define OUTPUT_PATH "/dev/tty" + +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); + close(term_fd); + term_fd = -1; +} + +static int disable_echo(void) +{ + 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); + + t.c_lflag &= ~ECHO; + if (!tcsetattr(term_fd, TCSAFLUSH, &t)) + return 0; + +error: + close(term_fd); + term_fd = -1; + return -1; +} + +#elif defined(GIT_WINDOWS_NATIVE) + +#define INPUT_PATH "CONIN$" +#define OUTPUT_PATH "CONOUT$" +#define FORCE_TEXT "t" + +static HANDLE hconin = INVALID_HANDLE_VALUE; +static DWORD cmode; + +static void restore_term(void) +{ + if (hconin == INVALID_HANDLE_VALUE) + return; + + SetConsoleMode(hconin, cmode); + CloseHandle(hconin); + hconin = INVALID_HANDLE_VALUE; +} + +static int disable_echo(void) +{ + 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); + if (!SetConsoleMode(hconin, cmode & (~ENABLE_ECHO_INPUT))) { + CloseHandle(hconin); + hconin = INVALID_HANDLE_VALUE; + return -1; + } + + return 0; +} + +#endif + +#ifndef FORCE_TEXT +#define FORCE_TEXT +#endif + +char *git_terminal_prompt(const char *prompt, int echo) +{ + static struct strbuf buf = STRBUF_INIT; + int r; + FILE *input_fh, *output_fh; + + input_fh = fopen(INPUT_PATH, "r" FORCE_TEXT); + if (!input_fh) + return NULL; + + output_fh = fopen(OUTPUT_PATH, "w" FORCE_TEXT); + if (!output_fh) { + fclose(input_fh); + return NULL; + } + + if (!echo && disable_echo()) { + fclose(input_fh); + fclose(output_fh); + return NULL; + } + + fputs(prompt, output_fh); + fflush(output_fh); + + r = strbuf_getline(&buf, input_fh, '\n'); + if (!echo) { + putc('\n', output_fh); + fflush(output_fh); + } + + restore_term(); + fclose(input_fh); + fclose(output_fh); + + if (r == EOF) + return NULL; + return buf.buf; +} + +#else + +char *git_terminal_prompt(const char *prompt, int echo) +{ + return getpass(prompt); +} + +#endif |