compat / terminal.con commit general improvements (43abf13)
   1#include "git-compat-util.h"
   2#include "compat/terminal.h"
   3#include "sigchain.h"
   4#include "strbuf.h"
   5
   6#if defined(HAVE_DEV_TTY) || defined(GIT_WINDOWS_NATIVE)
   7
   8static void restore_term(void);
   9
  10static void restore_term_on_signal(int sig)
  11{
  12        restore_term();
  13        sigchain_pop(sig);
  14        raise(sig);
  15}
  16
  17#ifdef HAVE_DEV_TTY
  18
  19#define INPUT_PATH "/dev/tty"
  20#define OUTPUT_PATH "/dev/tty"
  21
  22static int term_fd = -1;
  23static struct termios old_term;
  24
  25static void restore_term(void)
  26{
  27        if (term_fd < 0)
  28                return;
  29
  30        tcsetattr(term_fd, TCSAFLUSH, &old_term);
  31        close(term_fd);
  32        term_fd = -1;
  33}
  34
  35static int disable_echo(void)
  36{
  37        struct termios t;
  38
  39        term_fd = open("/dev/tty", O_RDWR);
  40        if (tcgetattr(term_fd, &t) < 0)
  41                goto error;
  42
  43        old_term = t;
  44        sigchain_push_common(restore_term_on_signal);
  45
  46        t.c_lflag &= ~ECHO;
  47        if (!tcsetattr(term_fd, TCSAFLUSH, &t))
  48                return 0;
  49
  50error:
  51        close(term_fd);
  52        term_fd = -1;
  53        return -1;
  54}
  55
  56#elif defined(GIT_WINDOWS_NATIVE)
  57
  58#define INPUT_PATH "CONIN$"
  59#define OUTPUT_PATH "CONOUT$"
  60#define FORCE_TEXT "t"
  61
  62static HANDLE hconin = INVALID_HANDLE_VALUE;
  63static DWORD cmode;
  64
  65static void restore_term(void)
  66{
  67        if (hconin == INVALID_HANDLE_VALUE)
  68                return;
  69
  70        SetConsoleMode(hconin, cmode);
  71        CloseHandle(hconin);
  72        hconin = INVALID_HANDLE_VALUE;
  73}
  74
  75static int disable_echo(void)
  76{
  77        hconin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
  78            FILE_SHARE_READ, NULL, OPEN_EXISTING,
  79            FILE_ATTRIBUTE_NORMAL, NULL);
  80        if (hconin == INVALID_HANDLE_VALUE)
  81                return -1;
  82
  83        GetConsoleMode(hconin, &cmode);
  84        sigchain_push_common(restore_term_on_signal);
  85        if (!SetConsoleMode(hconin, cmode & (~ENABLE_ECHO_INPUT))) {
  86                CloseHandle(hconin);
  87                hconin = INVALID_HANDLE_VALUE;
  88                return -1;
  89        }
  90
  91        return 0;
  92}
  93
  94#endif
  95
  96#ifndef FORCE_TEXT
  97#define FORCE_TEXT
  98#endif
  99
 100char *git_terminal_prompt(const char *prompt, int echo)
 101{
 102        static struct strbuf buf = STRBUF_INIT;
 103        int r;
 104        FILE *input_fh, *output_fh;
 105
 106        input_fh = fopen(INPUT_PATH, "r" FORCE_TEXT);
 107        if (!input_fh)
 108                return NULL;
 109
 110        output_fh = fopen(OUTPUT_PATH, "w" FORCE_TEXT);
 111        if (!output_fh) {
 112                fclose(input_fh);
 113                return NULL;
 114        }
 115
 116        if (!echo && disable_echo()) {
 117                fclose(input_fh);
 118                fclose(output_fh);
 119                return NULL;
 120        }
 121
 122        fputs(prompt, output_fh);
 123        fflush(output_fh);
 124
 125        r = strbuf_getline_lf(&buf, input_fh);
 126        if (!echo) {
 127                putc('\n', output_fh);
 128                fflush(output_fh);
 129        }
 130
 131        restore_term();
 132        fclose(input_fh);
 133        fclose(output_fh);
 134
 135        if (r == EOF)
 136                return NULL;
 137        return buf.buf;
 138}
 139
 140#else
 141
 142char *git_terminal_prompt(const char *prompt, int echo)
 143{
 144        return getpass(prompt);
 145}
 146
 147#endif