The eighth batch
[git/gitster.git] / compat / terminal.c
blob584f27bf7e1078338d5b7a0aedf12aecdc95fbd1
1 #include "git-compat-util.h"
2 #include "compat/terminal.h"
3 #include "gettext.h"
4 #include "sigchain.h"
5 #include "strbuf.h"
6 #include "run-command.h"
7 #include "string-list.h"
8 #include "hashmap.h"
10 #if defined(HAVE_DEV_TTY) || defined(GIT_WINDOWS_NATIVE)
12 static void restore_term_on_signal(int sig)
14 restore_term();
15 /* restore_term calls sigchain_pop_common */
16 raise(sig);
19 #ifdef HAVE_DEV_TTY
21 #define INPUT_PATH "/dev/tty"
22 #define OUTPUT_PATH "/dev/tty"
24 static volatile sig_atomic_t term_fd_needs_closing;
25 static int term_fd = -1;
26 static struct termios old_term;
28 static const char *background_resume_msg;
29 static const char *restore_error_msg;
30 static volatile sig_atomic_t ttou_received;
32 /* async safe error function for use by signal handlers. */
33 static void write_err(const char *msg)
35 write_in_full(2, "error: ", strlen("error: "));
36 write_in_full(2, msg, strlen(msg));
37 write_in_full(2, "\n", 1);
40 static void print_background_resume_msg(int signo)
42 int saved_errno = errno;
43 sigset_t mask;
44 struct sigaction old_sa;
45 struct sigaction sa = { .sa_handler = SIG_DFL };
47 ttou_received = 1;
48 write_err(background_resume_msg);
49 sigaction(signo, &sa, &old_sa);
50 raise(signo);
51 sigemptyset(&mask);
52 sigaddset(&mask, signo);
53 sigprocmask(SIG_UNBLOCK, &mask, NULL);
54 /* Stopped here */
55 sigprocmask(SIG_BLOCK, &mask, NULL);
56 sigaction(signo, &old_sa, NULL);
57 errno = saved_errno;
60 static void restore_terminal_on_suspend(int signo)
62 int saved_errno = errno;
63 int res;
64 struct termios t;
65 sigset_t mask;
66 struct sigaction old_sa;
67 struct sigaction sa = { .sa_handler = SIG_DFL };
68 int can_restore = 1;
70 if (tcgetattr(term_fd, &t) < 0)
71 can_restore = 0;
73 if (tcsetattr(term_fd, TCSAFLUSH, &old_term) < 0)
74 write_err(restore_error_msg);
76 sigaction(signo, &sa, &old_sa);
77 raise(signo);
78 sigemptyset(&mask);
79 sigaddset(&mask, signo);
80 sigprocmask(SIG_UNBLOCK, &mask, NULL);
81 /* Stopped here */
82 sigprocmask(SIG_BLOCK, &mask, NULL);
83 sigaction(signo, &old_sa, NULL);
84 if (!can_restore) {
85 write_err(restore_error_msg);
86 goto out;
89 * If we resume in the background then we receive SIGTTOU when calling
90 * tcsetattr() below. Set up a handler to print an error message in that
91 * case.
93 sigemptyset(&mask);
94 sigaddset(&mask, SIGTTOU);
95 sa.sa_mask = old_sa.sa_mask;
96 sa.sa_handler = print_background_resume_msg;
97 sa.sa_flags = SA_RESTART;
98 sigaction(SIGTTOU, &sa, &old_sa);
99 again:
100 ttou_received = 0;
101 sigprocmask(SIG_UNBLOCK, &mask, NULL);
102 res = tcsetattr(term_fd, TCSAFLUSH, &t);
103 sigprocmask(SIG_BLOCK, &mask, NULL);
104 if (ttou_received)
105 goto again;
106 else if (res < 0)
107 write_err(restore_error_msg);
108 sigaction(SIGTTOU, &old_sa, NULL);
109 out:
110 errno = saved_errno;
113 static void reset_job_signals(void)
115 if (restore_error_msg) {
116 signal(SIGTTIN, SIG_DFL);
117 signal(SIGTTOU, SIG_DFL);
118 signal(SIGTSTP, SIG_DFL);
119 restore_error_msg = NULL;
120 background_resume_msg = NULL;
124 static void close_term_fd(void)
126 if (term_fd_needs_closing)
127 close(term_fd);
128 term_fd_needs_closing = 0;
129 term_fd = -1;
132 void restore_term(void)
134 if (term_fd < 0)
135 return;
137 tcsetattr(term_fd, TCSAFLUSH, &old_term);
138 close_term_fd();
139 sigchain_pop_common();
140 reset_job_signals();
143 int save_term(enum save_term_flags flags)
145 struct sigaction sa;
147 if (term_fd < 0)
148 term_fd = ((flags & SAVE_TERM_STDIN)
150 : open("/dev/tty", O_RDWR));
151 if (term_fd < 0)
152 return -1;
153 term_fd_needs_closing = !(flags & SAVE_TERM_STDIN);
154 if (tcgetattr(term_fd, &old_term) < 0) {
155 close_term_fd();
156 return -1;
158 sigchain_push_common(restore_term_on_signal);
160 * If job control is disabled then the shell will have set the
161 * disposition of SIGTSTP to SIG_IGN.
163 sigaction(SIGTSTP, NULL, &sa);
164 if (sa.sa_handler == SIG_IGN)
165 return 0;
167 /* avoid calling gettext() from signal handler */
168 background_resume_msg = _("cannot resume in the background, please use 'fg' to resume");
169 restore_error_msg = _("cannot restore terminal settings");
170 sa.sa_handler = restore_terminal_on_suspend;
171 sa.sa_flags = SA_RESTART;
172 sigemptyset(&sa.sa_mask);
173 sigaddset(&sa.sa_mask, SIGTSTP);
174 sigaddset(&sa.sa_mask, SIGTTIN);
175 sigaddset(&sa.sa_mask, SIGTTOU);
176 sigaction(SIGTSTP, &sa, NULL);
177 sigaction(SIGTTIN, &sa, NULL);
178 sigaction(SIGTTOU, &sa, NULL);
180 return 0;
183 static int disable_bits(enum save_term_flags flags, tcflag_t bits)
185 struct termios t;
187 if (save_term(flags) < 0)
188 return -1;
190 t = old_term;
192 t.c_lflag &= ~bits;
193 if (bits & ICANON) {
194 t.c_cc[VMIN] = 1;
195 t.c_cc[VTIME] = 0;
197 if (!tcsetattr(term_fd, TCSAFLUSH, &t))
198 return 0;
200 sigchain_pop_common();
201 reset_job_signals();
202 close_term_fd();
203 return -1;
206 static int disable_echo(enum save_term_flags flags)
208 return disable_bits(flags, ECHO);
211 static int enable_non_canonical(enum save_term_flags flags)
213 return disable_bits(flags, ICANON | ECHO);
217 * On macos it is not possible to use poll() with a terminal so use select
218 * instead.
220 static int getchar_with_timeout(int timeout)
222 struct timeval tv, *tvp = NULL;
223 fd_set readfds;
224 int res;
226 again:
227 if (timeout >= 0) {
228 tv.tv_sec = timeout / 1000;
229 tv.tv_usec = (timeout % 1000) * 1000;
230 tvp = &tv;
233 FD_ZERO(&readfds);
234 FD_SET(0, &readfds);
235 res = select(1, &readfds, NULL, NULL, tvp);
236 if (!res)
237 return EOF;
238 if (res < 0) {
239 if (errno == EINTR)
240 goto again;
241 else
242 return EOF;
244 return getchar();
247 #elif defined(GIT_WINDOWS_NATIVE)
249 #define INPUT_PATH "CONIN$"
250 #define OUTPUT_PATH "CONOUT$"
251 #define FORCE_TEXT "t"
253 static int use_stty = 1;
254 static struct string_list stty_restore = STRING_LIST_INIT_DUP;
255 static HANDLE hconin = INVALID_HANDLE_VALUE;
256 static HANDLE hconout = INVALID_HANDLE_VALUE;
257 static DWORD cmode_in, cmode_out;
259 void restore_term(void)
261 if (use_stty) {
262 struct child_process cp = CHILD_PROCESS_INIT;
264 if (stty_restore.nr == 0)
265 return;
267 strvec_push(&cp.args, "stty");
268 for (size_t i = 0; i < stty_restore.nr; i++)
269 strvec_push(&cp.args, stty_restore.items[i].string);
270 run_command(&cp);
271 string_list_clear(&stty_restore, 0);
272 return;
275 sigchain_pop_common();
277 if (hconin == INVALID_HANDLE_VALUE)
278 return;
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);
288 hconin = hconout = INVALID_HANDLE_VALUE;
291 int save_term(enum save_term_flags flags)
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;
299 if (flags & SAVE_TERM_DUPLEX) {
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;
306 GetConsoleMode(hconout, &cmode_out);
309 GetConsoleMode(hconin, &cmode_in);
310 use_stty = 0;
311 sigchain_push_common(restore_term_on_signal);
312 return 0;
313 error:
314 CloseHandle(hconin);
315 hconin = INVALID_HANDLE_VALUE;
316 return -1;
319 static int disable_bits(enum save_term_flags flags, DWORD bits)
321 if (use_stty) {
322 struct child_process cp = CHILD_PROCESS_INIT;
324 strvec_push(&cp.args, "stty");
326 if (bits & ENABLE_LINE_INPUT) {
327 string_list_append(&stty_restore, "icanon");
329 * POSIX allows VMIN and VTIME to overlap with VEOF and
330 * VEOL - let's hope that is not the case on windows.
332 strvec_pushl(&cp.args, "-icanon", "min", "1", "time", "0", NULL);
335 if (bits & ENABLE_ECHO_INPUT) {
336 string_list_append(&stty_restore, "echo");
337 strvec_push(&cp.args, "-echo");
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");
344 strvec_push(&cp.args, "ignbrk");
345 strvec_push(&cp.args, "intr");
346 strvec_push(&cp.args, "");
349 if (run_command(&cp) == 0)
350 return 0;
352 /* `stty` could not be executed; access the Console directly */
353 use_stty = 0;
356 if (save_term(flags) < 0)
357 return -1;
359 if (!SetConsoleMode(hconin, cmode_in & ~bits)) {
360 CloseHandle(hconin);
361 hconin = INVALID_HANDLE_VALUE;
362 sigchain_pop_common();
363 return -1;
366 return 0;
369 static int disable_echo(enum save_term_flags flags)
371 return disable_bits(flags, ENABLE_ECHO_INPUT);
374 static int enable_non_canonical(enum save_term_flags flags)
376 return disable_bits(flags,
377 ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);
381 * Override `getchar()`, as the default implementation does not use
382 * `ReadFile()`.
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).
391 * So let's just override `getchar()` with a version backed by `ReadFile()` and
392 * go our merry ways from here.
394 static int mingw_getchar(void)
396 DWORD read = 0;
397 unsigned char ch;
399 if (!ReadFile(GetStdHandle(STD_INPUT_HANDLE), &ch, 1, &read, NULL))
400 return EOF;
402 if (!read) {
403 error("Unexpected 0 read");
404 return EOF;
407 return ch;
409 #define getchar mingw_getchar
411 static int getchar_with_timeout(int timeout)
413 struct pollfd pfd = { .fd = 0, .events = POLLIN };
415 if (poll(&pfd, 1, timeout) < 1)
416 return EOF;
418 return getchar();
421 #endif
423 #ifndef FORCE_TEXT
424 #define FORCE_TEXT
425 #endif
427 char *git_terminal_prompt(const char *prompt, int echo)
429 static struct strbuf buf = STRBUF_INIT;
430 int r;
431 FILE *input_fh, *output_fh;
433 input_fh = fopen(INPUT_PATH, "r" FORCE_TEXT);
434 if (!input_fh)
435 return NULL;
437 output_fh = fopen(OUTPUT_PATH, "w" FORCE_TEXT);
438 if (!output_fh) {
439 fclose(input_fh);
440 return NULL;
443 if (!echo && disable_echo(0)) {
444 fclose(input_fh);
445 fclose(output_fh);
446 return NULL;
449 fputs(prompt, output_fh);
450 fflush(output_fh);
452 r = strbuf_getline_lf(&buf, input_fh);
453 if (!echo) {
454 putc('\n', output_fh);
455 fflush(output_fh);
458 restore_term();
459 fclose(input_fh);
460 fclose(output_fh);
462 if (r == EOF)
463 return NULL;
464 return buf.buf;
468 * The `is_known_escape_sequence()` function returns 1 if the passed string
469 * corresponds to an Escape sequence that the terminal capabilities contains.
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).
475 struct escape_sequence_entry {
476 struct hashmap_entry entry;
477 char sequence[FLEX_ARRAY];
480 static int sequence_entry_cmp(const void *hashmap_cmp_fn_data UNUSED,
481 const struct hashmap_entry *he1,
482 const struct hashmap_entry *he2,
483 const void *keydata)
485 const struct escape_sequence_entry
486 *e1 = container_of(he1, const struct escape_sequence_entry, entry),
487 *e2 = container_of(he2, const struct escape_sequence_entry, entry);
488 return strcmp(e1->sequence, keydata ? keydata : e2->sequence);
491 static int is_known_escape_sequence(const char *sequence)
493 static struct hashmap sequences;
494 static int initialized;
496 if (!initialized) {
497 struct child_process cp = CHILD_PROCESS_INIT;
498 struct strbuf buf = STRBUF_INIT;
499 char *p, *eol;
501 hashmap_init(&sequences, sequence_entry_cmp, NULL, 0);
503 strvec_pushl(&cp.args, "infocmp", "-L", "-1", NULL);
504 if (pipe_command(&cp, NULL, 0, &buf, 0, NULL, 0))
505 strbuf_setlen(&buf, 0);
507 for (eol = p = buf.buf; *p; p = eol + 1) {
508 p = strchr(p, '=');
509 if (!p)
510 break;
511 p++;
512 eol = strchrnul(p, '\n');
514 if (starts_with(p, "\\E")) {
515 char *comma = memchr(p, ',', eol - p);
516 struct escape_sequence_entry *e;
518 p[0] = '^';
519 p[1] = '[';
520 FLEX_ALLOC_MEM(e, sequence, p, comma - p);
521 hashmap_entry_init(&e->entry,
522 strhash(e->sequence));
523 hashmap_add(&sequences, &e->entry);
525 if (!*eol)
526 break;
528 initialized = 1;
531 return !!hashmap_get_from_hash(&sequences, strhash(sequence), sequence);
534 int read_key_without_echo(struct strbuf *buf)
536 static int warning_displayed;
537 int ch;
539 if (warning_displayed || enable_non_canonical(SAVE_TERM_STDIN) < 0) {
540 if (!warning_displayed) {
541 warning("reading single keystrokes not supported on "
542 "this platform; reading line instead");
543 warning_displayed = 1;
546 return strbuf_getline(buf, stdin);
549 strbuf_reset(buf);
550 ch = getchar();
551 if (ch == EOF) {
552 restore_term();
553 return EOF;
555 strbuf_addch(buf, ch);
557 if (ch == '\033' /* ESC */) {
559 * We are most likely looking at an Escape sequence. Let's try
560 * to read more bytes, waiting at most half a second, assuming
561 * that the sequence is complete if we did not receive any byte
562 * within that time.
564 * Start by replacing the Escape byte with ^[ */
565 strbuf_splice(buf, buf->len - 1, 1, "^[", 2);
568 * Query the terminal capabilities once about all the Escape
569 * sequences it knows about, so that we can avoid waiting for
570 * half a second when we know that the sequence is complete.
572 while (!is_known_escape_sequence(buf->buf)) {
573 ch = getchar_with_timeout(500);
574 if (ch == EOF)
575 break;
576 strbuf_addch(buf, ch);
580 restore_term();
581 return 0;
584 #else
586 int save_term(enum save_term_flags flags)
588 /* no duplex support available */
589 return -!!(flags & SAVE_TERM_DUPLEX);
592 void restore_term(void)
596 char *git_terminal_prompt(const char *prompt, int echo UNUSED)
598 return getpass(prompt);
601 int read_key_without_echo(struct strbuf *buf)
603 static int warning_displayed;
604 const char *res;
606 if (!warning_displayed) {
607 warning("reading single keystrokes not supported on this "
608 "platform; reading line instead");
609 warning_displayed = 1;
612 res = getpass("");
613 strbuf_reset(buf);
614 if (!res)
615 return EOF;
616 strbuf_addstr(buf, res);
617 return 0;
620 #endif