]>
Commit | Line | Data |
---|---|---|
0f584deb | 1 | #include "cache.h" |
21aeafce JK |
2 | #include "compat/terminal.h" |
3 | #include "sigchain.h" | |
4 | #include "strbuf.h" | |
9ea416cb JS |
5 | #include "run-command.h" |
6 | #include "string-list.h" | |
12acdf57 | 7 | #include "hashmap.h" |
21aeafce | 8 | |
380395d0 | 9 | #if defined(HAVE_DEV_TTY) || defined(GIT_WINDOWS_NATIVE) |
afb43561 | 10 | |
afb43561 EFL |
11 | static void restore_term_on_signal(int sig) |
12 | { | |
13 | restore_term(); | |
f7da7565 | 14 | /* restore_term calls sigchain_pop_common */ |
afb43561 EFL |
15 | raise(sig); |
16 | } | |
17 | ||
21aeafce JK |
18 | #ifdef HAVE_DEV_TTY |
19 | ||
afb43561 EFL |
20 | #define INPUT_PATH "/dev/tty" |
21 | #define OUTPUT_PATH "/dev/tty" | |
22 | ||
e4938ce3 | 23 | static volatile sig_atomic_t term_fd_needs_closing; |
21aeafce JK |
24 | static int term_fd = -1; |
25 | static struct termios old_term; | |
26 | ||
0f584deb PW |
27 | static const char *background_resume_msg; |
28 | static const char *restore_error_msg; | |
29 | static volatile sig_atomic_t ttou_received; | |
30 | ||
31 | /* async safe error function for use by signal handlers. */ | |
32 | static void write_err(const char *msg) | |
33 | { | |
34 | write_in_full(2, "error: ", strlen("error: ")); | |
35 | write_in_full(2, msg, strlen(msg)); | |
36 | write_in_full(2, "\n", 1); | |
37 | } | |
38 | ||
39 | static void print_background_resume_msg(int signo) | |
40 | { | |
41 | int saved_errno = errno; | |
42 | sigset_t mask; | |
43 | struct sigaction old_sa; | |
44 | struct sigaction sa = { .sa_handler = SIG_DFL }; | |
45 | ||
46 | ttou_received = 1; | |
47 | write_err(background_resume_msg); | |
48 | sigaction(signo, &sa, &old_sa); | |
49 | raise(signo); | |
50 | sigemptyset(&mask); | |
51 | sigaddset(&mask, signo); | |
52 | sigprocmask(SIG_UNBLOCK, &mask, NULL); | |
53 | /* Stopped here */ | |
54 | sigprocmask(SIG_BLOCK, &mask, NULL); | |
55 | sigaction(signo, &old_sa, NULL); | |
56 | errno = saved_errno; | |
57 | } | |
58 | ||
59 | static void restore_terminal_on_suspend(int signo) | |
60 | { | |
61 | int saved_errno = errno; | |
62 | int res; | |
63 | struct termios t; | |
64 | sigset_t mask; | |
65 | struct sigaction old_sa; | |
66 | struct sigaction sa = { .sa_handler = SIG_DFL }; | |
67 | int can_restore = 1; | |
68 | ||
69 | if (tcgetattr(term_fd, &t) < 0) | |
70 | can_restore = 0; | |
71 | ||
72 | if (tcsetattr(term_fd, TCSAFLUSH, &old_term) < 0) | |
73 | write_err(restore_error_msg); | |
74 | ||
75 | sigaction(signo, &sa, &old_sa); | |
76 | raise(signo); | |
77 | sigemptyset(&mask); | |
78 | sigaddset(&mask, signo); | |
79 | sigprocmask(SIG_UNBLOCK, &mask, NULL); | |
80 | /* Stopped here */ | |
81 | sigprocmask(SIG_BLOCK, &mask, NULL); | |
82 | sigaction(signo, &old_sa, NULL); | |
83 | if (!can_restore) { | |
84 | write_err(restore_error_msg); | |
85 | goto out; | |
86 | } | |
87 | /* | |
88 | * If we resume in the background then we receive SIGTTOU when calling | |
89 | * tcsetattr() below. Set up a handler to print an error message in that | |
90 | * case. | |
91 | */ | |
92 | sigemptyset(&mask); | |
93 | sigaddset(&mask, SIGTTOU); | |
94 | sa.sa_mask = old_sa.sa_mask; | |
95 | sa.sa_handler = print_background_resume_msg; | |
96 | sa.sa_flags = SA_RESTART; | |
97 | sigaction(SIGTTOU, &sa, &old_sa); | |
98 | again: | |
99 | ttou_received = 0; | |
100 | sigprocmask(SIG_UNBLOCK, &mask, NULL); | |
101 | res = tcsetattr(term_fd, TCSAFLUSH, &t); | |
102 | sigprocmask(SIG_BLOCK, &mask, NULL); | |
103 | if (ttou_received) | |
104 | goto again; | |
105 | else if (res < 0) | |
106 | write_err(restore_error_msg); | |
107 | sigaction(SIGTTOU, &old_sa, NULL); | |
108 | out: | |
109 | errno = saved_errno; | |
110 | } | |
111 | ||
112 | static void reset_job_signals(void) | |
113 | { | |
114 | if (restore_error_msg) { | |
115 | signal(SIGTTIN, SIG_DFL); | |
116 | signal(SIGTTOU, SIG_DFL); | |
117 | signal(SIGTSTP, SIG_DFL); | |
118 | restore_error_msg = NULL; | |
119 | background_resume_msg = NULL; | |
120 | } | |
121 | } | |
122 | ||
e4938ce3 PW |
123 | static void close_term_fd(void) |
124 | { | |
125 | if (term_fd_needs_closing) | |
126 | close(term_fd); | |
127 | term_fd_needs_closing = 0; | |
128 | term_fd = -1; | |
129 | } | |
130 | ||
e22b245e | 131 | void restore_term(void) |
21aeafce JK |
132 | { |
133 | if (term_fd < 0) | |
134 | return; | |
135 | ||
136 | tcsetattr(term_fd, TCSAFLUSH, &old_term); | |
e4938ce3 | 137 | close_term_fd(); |
f7da7565 | 138 | sigchain_pop_common(); |
0f584deb | 139 | reset_job_signals(); |
21aeafce JK |
140 | } |
141 | ||
02af15de | 142 | int save_term(enum save_term_flags flags) |
e22b245e | 143 | { |
0f584deb PW |
144 | struct sigaction sa; |
145 | ||
e22b245e | 146 | if (term_fd < 0) |
e4938ce3 PW |
147 | term_fd = ((flags & SAVE_TERM_STDIN) |
148 | ? 0 | |
149 | : open("/dev/tty", O_RDWR)); | |
f7da7565 PW |
150 | if (term_fd < 0) |
151 | return -1; | |
e4938ce3 PW |
152 | term_fd_needs_closing = !(flags & SAVE_TERM_STDIN); |
153 | if (tcgetattr(term_fd, &old_term) < 0) { | |
154 | close_term_fd(); | |
f7da7565 | 155 | return -1; |
e4938ce3 | 156 | } |
f7da7565 | 157 | sigchain_push_common(restore_term_on_signal); |
0f584deb PW |
158 | /* |
159 | * If job control is disabled then the shell will have set the | |
160 | * disposition of SIGTSTP to SIG_IGN. | |
161 | */ | |
162 | sigaction(SIGTSTP, NULL, &sa); | |
163 | if (sa.sa_handler == SIG_IGN) | |
164 | return 0; | |
165 | ||
166 | /* avoid calling gettext() from signal handler */ | |
167 | background_resume_msg = _("cannot resume in the background, please use 'fg' to resume"); | |
168 | restore_error_msg = _("cannot restore terminal settings"); | |
169 | sa.sa_handler = restore_terminal_on_suspend; | |
170 | sa.sa_flags = SA_RESTART; | |
171 | sigemptyset(&sa.sa_mask); | |
172 | sigaddset(&sa.sa_mask, SIGTSTP); | |
173 | sigaddset(&sa.sa_mask, SIGTTIN); | |
174 | sigaddset(&sa.sa_mask, SIGTTOU); | |
175 | sigaction(SIGTSTP, &sa, NULL); | |
176 | sigaction(SIGTTIN, &sa, NULL); | |
177 | sigaction(SIGTTOU, &sa, NULL); | |
e22b245e | 178 | |
f7da7565 | 179 | return 0; |
e22b245e CMAB |
180 | } |
181 | ||
02af15de | 182 | static int disable_bits(enum save_term_flags flags, tcflag_t bits) |
9df92e63 EFL |
183 | { |
184 | struct termios t; | |
185 | ||
02af15de | 186 | if (save_term(flags) < 0) |
e4938ce3 | 187 | return -1; |
9df92e63 | 188 | |
e22b245e | 189 | t = old_term; |
9df92e63 | 190 | |
94ac3c31 | 191 | t.c_lflag &= ~bits; |
2c686021 PW |
192 | if (bits & ICANON) { |
193 | t.c_cc[VMIN] = 1; | |
194 | t.c_cc[VTIME] = 0; | |
195 | } | |
9df92e63 EFL |
196 | if (!tcsetattr(term_fd, TCSAFLUSH, &t)) |
197 | return 0; | |
198 | ||
f7da7565 | 199 | sigchain_pop_common(); |
0f584deb | 200 | reset_job_signals(); |
e4938ce3 | 201 | close_term_fd(); |
9df92e63 EFL |
202 | return -1; |
203 | } | |
204 | ||
02af15de | 205 | static int disable_echo(enum save_term_flags flags) |
94ac3c31 | 206 | { |
02af15de | 207 | return disable_bits(flags, ECHO); |
94ac3c31 JS |
208 | } |
209 | ||
02af15de | 210 | static int enable_non_canonical(enum save_term_flags flags) |
a5e46e6b | 211 | { |
02af15de | 212 | return disable_bits(flags, ICANON | ECHO); |
a5e46e6b JS |
213 | } |
214 | ||
6606d99b PW |
215 | /* |
216 | * On macos it is not possible to use poll() with a terminal so use select | |
217 | * instead. | |
218 | */ | |
219 | static int getchar_with_timeout(int timeout) | |
220 | { | |
221 | struct timeval tv, *tvp = NULL; | |
222 | fd_set readfds; | |
223 | int res; | |
224 | ||
0f584deb | 225 | again: |
6606d99b PW |
226 | if (timeout >= 0) { |
227 | tv.tv_sec = timeout / 1000; | |
228 | tv.tv_usec = (timeout % 1000) * 1000; | |
229 | tvp = &tv; | |
230 | } | |
231 | ||
232 | FD_ZERO(&readfds); | |
233 | FD_SET(0, &readfds); | |
234 | res = select(1, &readfds, NULL, NULL, tvp); | |
0f584deb | 235 | if (!res) |
6606d99b | 236 | return EOF; |
0f584deb PW |
237 | if (res < 0) { |
238 | if (errno == EINTR) | |
239 | goto again; | |
240 | else | |
241 | return EOF; | |
242 | } | |
6606d99b PW |
243 | return getchar(); |
244 | } | |
245 | ||
380395d0 | 246 | #elif defined(GIT_WINDOWS_NATIVE) |
afb43561 EFL |
247 | |
248 | #define INPUT_PATH "CONIN$" | |
249 | #define OUTPUT_PATH "CONOUT$" | |
250 | #define FORCE_TEXT "t" | |
251 | ||
9ea416cb JS |
252 | static int use_stty = 1; |
253 | static struct string_list stty_restore = STRING_LIST_INIT_DUP; | |
afb43561 | 254 | static HANDLE hconin = INVALID_HANDLE_VALUE; |
e22b245e CMAB |
255 | static HANDLE hconout = INVALID_HANDLE_VALUE; |
256 | static DWORD cmode_in, cmode_out; | |
afb43561 | 257 | |
e22b245e | 258 | void restore_term(void) |
afb43561 | 259 | { |
9ea416cb JS |
260 | if (use_stty) { |
261 | int i; | |
262 | struct child_process cp = CHILD_PROCESS_INIT; | |
263 | ||
264 | if (stty_restore.nr == 0) | |
265 | return; | |
266 | ||
ef8d7ac4 | 267 | strvec_push(&cp.args, "stty"); |
9ea416cb | 268 | for (i = 0; i < stty_restore.nr; i++) |
ef8d7ac4 | 269 | strvec_push(&cp.args, stty_restore.items[i].string); |
9ea416cb JS |
270 | run_command(&cp); |
271 | string_list_clear(&stty_restore, 0); | |
272 | return; | |
273 | } | |
274 | ||
f7da7565 PW |
275 | sigchain_pop_common(); |
276 | ||
afb43561 EFL |
277 | if (hconin == INVALID_HANDLE_VALUE) |
278 | return; | |
279 | ||
e22b245e CMAB |
280 | SetConsoleMode(hconin, cmode_in); |
281 | CloseHandle(hconin); | |
282 | if (cmode_out) { | |
283 | assert(hconout != INVALID_HANDLE_VALUE); | |
284 | SetConsoleMode(hconout, cmode_out); | |
285 | CloseHandle(hconout); | |
286 | } | |
287 | ||
288 | hconin = hconout = INVALID_HANDLE_VALUE; | |
289 | } | |
290 | ||
02af15de | 291 | int save_term(enum save_term_flags flags) |
e22b245e CMAB |
292 | { |
293 | hconin = CreateFileA("CONIN$", GENERIC_READ | GENERIC_WRITE, | |
294 | FILE_SHARE_READ, NULL, OPEN_EXISTING, | |
295 | FILE_ATTRIBUTE_NORMAL, NULL); | |
296 | if (hconin == INVALID_HANDLE_VALUE) | |
297 | return -1; | |
298 | ||
02af15de | 299 | if (flags & SAVE_TERM_DUPLEX) { |
e22b245e CMAB |
300 | hconout = CreateFileA("CONOUT$", GENERIC_READ | GENERIC_WRITE, |
301 | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, | |
302 | FILE_ATTRIBUTE_NORMAL, NULL); | |
303 | if (hconout == INVALID_HANDLE_VALUE) | |
304 | goto error; | |
305 | ||
306 | GetConsoleMode(hconout, &cmode_out); | |
307 | } | |
308 | ||
309 | GetConsoleMode(hconin, &cmode_in); | |
310 | use_stty = 0; | |
f7da7565 | 311 | sigchain_push_common(restore_term_on_signal); |
e22b245e CMAB |
312 | return 0; |
313 | error: | |
afb43561 EFL |
314 | CloseHandle(hconin); |
315 | hconin = INVALID_HANDLE_VALUE; | |
e22b245e | 316 | return -1; |
afb43561 EFL |
317 | } |
318 | ||
02af15de | 319 | static int disable_bits(enum save_term_flags flags, DWORD bits) |
afb43561 | 320 | { |
9ea416cb JS |
321 | if (use_stty) { |
322 | struct child_process cp = CHILD_PROCESS_INIT; | |
323 | ||
ef8d7ac4 | 324 | strvec_push(&cp.args, "stty"); |
9ea416cb JS |
325 | |
326 | if (bits & ENABLE_LINE_INPUT) { | |
327 | string_list_append(&stty_restore, "icanon"); | |
2c686021 PW |
328 | /* |
329 | * POSIX allows VMIN and VTIME to overlap with VEOF and | |
330 | * VEOL - let's hope that is not the case on windows. | |
331 | */ | |
332 | strvec_pushl(&cp.args, "-icanon", "min", "1", "time", "0", NULL); | |
9ea416cb JS |
333 | } |
334 | ||
335 | if (bits & ENABLE_ECHO_INPUT) { | |
336 | string_list_append(&stty_restore, "echo"); | |
ef8d7ac4 | 337 | strvec_push(&cp.args, "-echo"); |
9ea416cb JS |
338 | } |
339 | ||
340 | if (bits & ENABLE_PROCESSED_INPUT) { | |
341 | string_list_append(&stty_restore, "-ignbrk"); | |
342 | string_list_append(&stty_restore, "intr"); | |
343 | string_list_append(&stty_restore, "^c"); | |
ef8d7ac4 JK |
344 | strvec_push(&cp.args, "ignbrk"); |
345 | strvec_push(&cp.args, "intr"); | |
346 | strvec_push(&cp.args, ""); | |
9ea416cb JS |
347 | } |
348 | ||
349 | if (run_command(&cp) == 0) | |
350 | return 0; | |
351 | ||
352 | /* `stty` could not be executed; access the Console directly */ | |
353 | use_stty = 0; | |
354 | } | |
355 | ||
02af15de | 356 | if (save_term(flags) < 0) |
afb43561 EFL |
357 | return -1; |
358 | ||
e22b245e | 359 | if (!SetConsoleMode(hconin, cmode_in & ~bits)) { |
afb43561 EFL |
360 | CloseHandle(hconin); |
361 | hconin = INVALID_HANDLE_VALUE; | |
f7da7565 | 362 | sigchain_pop_common(); |
afb43561 EFL |
363 | return -1; |
364 | } | |
365 | ||
366 | return 0; | |
367 | } | |
368 | ||
02af15de | 369 | static int disable_echo(enum save_term_flags flags) |
94ac3c31 | 370 | { |
02af15de | 371 | return disable_bits(flags, ENABLE_ECHO_INPUT); |
94ac3c31 JS |
372 | } |
373 | ||
02af15de | 374 | static int enable_non_canonical(enum save_term_flags flags) |
a5e46e6b | 375 | { |
02af15de PW |
376 | return disable_bits(flags, |
377 | ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT); | |
a5e46e6b | 378 | } |
94ac3c31 | 379 | |
e118f063 JS |
380 | /* |
381 | * Override `getchar()`, as the default implementation does not use | |
382 | * `ReadFile()`. | |
383 | * | |
384 | * This poses a problem when we want to see whether the standard | |
385 | * input has more characters, as the default of Git for Windows is to start the | |
386 | * Bash in a MinTTY, which uses a named pipe to emulate a pty, in which case | |
387 | * our `poll()` emulation calls `PeekNamedPipe()`, which seems to require | |
388 | * `ReadFile()` to be called first to work properly (it only reports 0 | |
389 | * available bytes, otherwise). | |
390 | * | |
391 | * So let's just override `getchar()` with a version backed by `ReadFile()` and | |
392 | * go our merry ways from here. | |
393 | */ | |
394 | static int mingw_getchar(void) | |
395 | { | |
396 | DWORD read = 0; | |
397 | unsigned char ch; | |
398 | ||
399 | if (!ReadFile(GetStdHandle(STD_INPUT_HANDLE), &ch, 1, &read, NULL)) | |
400 | return EOF; | |
401 | ||
402 | if (!read) { | |
403 | error("Unexpected 0 read"); | |
404 | return EOF; | |
405 | } | |
406 | ||
407 | return ch; | |
408 | } | |
409 | #define getchar mingw_getchar | |
410 | ||
6606d99b PW |
411 | static int getchar_with_timeout(int timeout) |
412 | { | |
413 | struct pollfd pfd = { .fd = 0, .events = POLLIN }; | |
414 | ||
415 | if (poll(&pfd, 1, timeout) < 1) | |
416 | return EOF; | |
417 | ||
418 | return getchar(); | |
419 | } | |
420 | ||
afb43561 EFL |
421 | #endif |
422 | ||
423 | #ifndef FORCE_TEXT | |
424 | #define FORCE_TEXT | |
425 | #endif | |
426 | ||
21aeafce JK |
427 | char *git_terminal_prompt(const char *prompt, int echo) |
428 | { | |
429 | static struct strbuf buf = STRBUF_INIT; | |
430 | int r; | |
67fe7356 | 431 | FILE *input_fh, *output_fh; |
21aeafce | 432 | |
afb43561 | 433 | input_fh = fopen(INPUT_PATH, "r" FORCE_TEXT); |
67fe7356 | 434 | if (!input_fh) |
21aeafce JK |
435 | return NULL; |
436 | ||
afb43561 | 437 | output_fh = fopen(OUTPUT_PATH, "w" FORCE_TEXT); |
67fe7356 EFL |
438 | if (!output_fh) { |
439 | fclose(input_fh); | |
440 | return NULL; | |
441 | } | |
442 | ||
02af15de | 443 | if (!echo && disable_echo(0)) { |
67fe7356 EFL |
444 | fclose(input_fh); |
445 | fclose(output_fh); | |
9df92e63 | 446 | return NULL; |
21aeafce JK |
447 | } |
448 | ||
67fe7356 EFL |
449 | fputs(prompt, output_fh); |
450 | fflush(output_fh); | |
21aeafce | 451 | |
8f309aeb | 452 | r = strbuf_getline_lf(&buf, input_fh); |
21aeafce | 453 | if (!echo) { |
67fe7356 EFL |
454 | putc('\n', output_fh); |
455 | fflush(output_fh); | |
21aeafce JK |
456 | } |
457 | ||
458 | restore_term(); | |
67fe7356 EFL |
459 | fclose(input_fh); |
460 | fclose(output_fh); | |
21aeafce JK |
461 | |
462 | if (r == EOF) | |
463 | return NULL; | |
464 | return buf.buf; | |
465 | } | |
466 | ||
12acdf57 JS |
467 | /* |
468 | * The `is_known_escape_sequence()` function returns 1 if the passed string | |
469 | * corresponds to an Escape sequence that the terminal capabilities contains. | |
470 | * | |
471 | * To avoid depending on ncurses or other platform-specific libraries, we rely | |
472 | * on the presence of the `infocmp` executable to do the job for us (failing | |
473 | * silently if the program is not available or refused to run). | |
474 | */ | |
475 | struct escape_sequence_entry { | |
476 | struct hashmap_entry entry; | |
477 | char sequence[FLEX_ARRAY]; | |
478 | }; | |
479 | ||
5cf88fd8 | 480 | static int sequence_entry_cmp(const void *hashmap_cmp_fn_data UNUSED, |
12acdf57 JS |
481 | const struct escape_sequence_entry *e1, |
482 | const struct escape_sequence_entry *e2, | |
483 | const void *keydata) | |
484 | { | |
485 | return strcmp(e1->sequence, keydata ? keydata : e2->sequence); | |
486 | } | |
487 | ||
488 | static int is_known_escape_sequence(const char *sequence) | |
489 | { | |
490 | static struct hashmap sequences; | |
491 | static int initialized; | |
492 | ||
493 | if (!initialized) { | |
494 | struct child_process cp = CHILD_PROCESS_INIT; | |
495 | struct strbuf buf = STRBUF_INIT; | |
496 | char *p, *eol; | |
497 | ||
498 | hashmap_init(&sequences, (hashmap_cmp_fn)sequence_entry_cmp, | |
499 | NULL, 0); | |
500 | ||
ef8d7ac4 | 501 | strvec_pushl(&cp.args, "infocmp", "-L", "-1", NULL); |
12acdf57 JS |
502 | if (pipe_command(&cp, NULL, 0, &buf, 0, NULL, 0)) |
503 | strbuf_setlen(&buf, 0); | |
504 | ||
505 | for (eol = p = buf.buf; *p; p = eol + 1) { | |
506 | p = strchr(p, '='); | |
507 | if (!p) | |
508 | break; | |
509 | p++; | |
510 | eol = strchrnul(p, '\n'); | |
511 | ||
512 | if (starts_with(p, "\\E")) { | |
513 | char *comma = memchr(p, ',', eol - p); | |
514 | struct escape_sequence_entry *e; | |
515 | ||
516 | p[0] = '^'; | |
517 | p[1] = '['; | |
518 | FLEX_ALLOC_MEM(e, sequence, p, comma - p); | |
519 | hashmap_entry_init(&e->entry, | |
520 | strhash(e->sequence)); | |
521 | hashmap_add(&sequences, &e->entry); | |
522 | } | |
523 | if (!*eol) | |
524 | break; | |
525 | } | |
526 | initialized = 1; | |
527 | } | |
528 | ||
529 | return !!hashmap_get_from_hash(&sequences, strhash(sequence), sequence); | |
530 | } | |
531 | ||
a5e46e6b JS |
532 | int read_key_without_echo(struct strbuf *buf) |
533 | { | |
534 | static int warning_displayed; | |
535 | int ch; | |
536 | ||
e4938ce3 | 537 | if (warning_displayed || enable_non_canonical(SAVE_TERM_STDIN) < 0) { |
a5e46e6b JS |
538 | if (!warning_displayed) { |
539 | warning("reading single keystrokes not supported on " | |
540 | "this platform; reading line instead"); | |
541 | warning_displayed = 1; | |
542 | } | |
543 | ||
544 | return strbuf_getline(buf, stdin); | |
545 | } | |
546 | ||
547 | strbuf_reset(buf); | |
548 | ch = getchar(); | |
549 | if (ch == EOF) { | |
550 | restore_term(); | |
551 | return EOF; | |
552 | } | |
a5e46e6b | 553 | strbuf_addch(buf, ch); |
e118f063 JS |
554 | |
555 | if (ch == '\033' /* ESC */) { | |
556 | /* | |
557 | * We are most likely looking at an Escape sequence. Let's try | |
558 | * to read more bytes, waiting at most half a second, assuming | |
559 | * that the sequence is complete if we did not receive any byte | |
560 | * within that time. | |
561 | * | |
562 | * Start by replacing the Escape byte with ^[ */ | |
563 | strbuf_splice(buf, buf->len - 1, 1, "^[", 2); | |
564 | ||
12acdf57 JS |
565 | /* |
566 | * Query the terminal capabilities once about all the Escape | |
567 | * sequences it knows about, so that we can avoid waiting for | |
568 | * half a second when we know that the sequence is complete. | |
569 | */ | |
570 | while (!is_known_escape_sequence(buf->buf)) { | |
6606d99b | 571 | ch = getchar_with_timeout(500); |
e118f063 | 572 | if (ch == EOF) |
24d7ce38 | 573 | break; |
e118f063 JS |
574 | strbuf_addch(buf, ch); |
575 | } | |
576 | } | |
577 | ||
a5e46e6b JS |
578 | restore_term(); |
579 | return 0; | |
580 | } | |
581 | ||
21aeafce JK |
582 | #else |
583 | ||
02af15de | 584 | int save_term(enum save_term_flags flags) |
e22b245e | 585 | { |
02af15de PW |
586 | /* no duplex support available */ |
587 | return -!!(flags & SAVE_TERM_DUPLEX); | |
e22b245e CMAB |
588 | } |
589 | ||
590 | void restore_term(void) | |
591 | { | |
592 | } | |
593 | ||
21aeafce JK |
594 | char *git_terminal_prompt(const char *prompt, int echo) |
595 | { | |
596 | return getpass(prompt); | |
597 | } | |
598 | ||
a5e46e6b JS |
599 | int read_key_without_echo(struct strbuf *buf) |
600 | { | |
601 | static int warning_displayed; | |
602 | const char *res; | |
603 | ||
604 | if (!warning_displayed) { | |
605 | warning("reading single keystrokes not supported on this " | |
606 | "platform; reading line instead"); | |
607 | warning_displayed = 1; | |
608 | } | |
609 | ||
610 | res = getpass(""); | |
611 | strbuf_reset(buf); | |
612 | if (!res) | |
613 | return EOF; | |
614 | strbuf_addstr(buf, res); | |
615 | return 0; | |
616 | } | |
617 | ||
21aeafce | 618 | #endif |