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
8 static void restore_term(void);
9
restore_term_on_signal(int sig)10 static 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
22 static int term_fd = -1;
23 static struct termios old_term;
24
restore_term(void)25 static 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
disable_echo(void)35 static 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
50 error:
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
62 static HANDLE hconin = INVALID_HANDLE_VALUE;
63 static DWORD cmode;
64
restore_term(void)65 static 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
disable_echo(void)75 static 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
git_terminal_prompt(const char * prompt,int echo)100 char *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
git_terminal_prompt(const char * prompt,int echo)142 char *git_terminal_prompt(const char *prompt, int echo)
143 {
144 return getpass(prompt);
145 }
146
147 #endif
148