#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_lf(&buf, input_fh); 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