Jeff King | 21aeafc | 2011-12-10 05:41:01 -0500 | [diff] [blame] | 1 | #include "git-compat-util.h" |
| 2 | #include "compat/terminal.h" |
| 3 | #include "sigchain.h" |
| 4 | #include "strbuf.h" |
Johannes Schindelin | 9ea416c | 2020-01-14 18:43:48 +0000 | [diff] [blame] | 5 | #include "run-command.h" |
| 6 | #include "string-list.h" |
Johannes Schindelin | 12acdf5 | 2020-01-14 18:43:52 +0000 | [diff] [blame] | 7 | #include "hashmap.h" |
Jeff King | 21aeafc | 2011-12-10 05:41:01 -0500 | [diff] [blame] | 8 | |
Jonathan Nieder | 380395d | 2013-05-02 20:26:08 +0100 | [diff] [blame] | 9 | #if defined(HAVE_DEV_TTY) || defined(GIT_WINDOWS_NATIVE) |
Erik Faye-Lund | afb4356 | 2012-12-04 09:10:41 +0100 | [diff] [blame] | 10 | |
Erik Faye-Lund | afb4356 | 2012-12-04 09:10:41 +0100 | [diff] [blame] | 11 | static void restore_term_on_signal(int sig) |
| 12 | { |
| 13 | restore_term(); |
| 14 | sigchain_pop(sig); |
| 15 | raise(sig); |
| 16 | } |
| 17 | |
Jeff King | 21aeafc | 2011-12-10 05:41:01 -0500 | [diff] [blame] | 18 | #ifdef HAVE_DEV_TTY |
| 19 | |
Erik Faye-Lund | afb4356 | 2012-12-04 09:10:41 +0100 | [diff] [blame] | 20 | #define INPUT_PATH "/dev/tty" |
| 21 | #define OUTPUT_PATH "/dev/tty" |
| 22 | |
Jeff King | 21aeafc | 2011-12-10 05:41:01 -0500 | [diff] [blame] | 23 | static int term_fd = -1; |
| 24 | static struct termios old_term; |
| 25 | |
Carlo Marcelo Arenas Belón | e22b245 | 2021-10-05 00:46:47 -0700 | [diff] [blame] | 26 | void restore_term(void) |
Jeff King | 21aeafc | 2011-12-10 05:41:01 -0500 | [diff] [blame] | 27 | { |
| 28 | if (term_fd < 0) |
| 29 | return; |
| 30 | |
| 31 | tcsetattr(term_fd, TCSAFLUSH, &old_term); |
Erik Faye-Lund | 9df92e6 | 2012-12-04 09:10:39 +0100 | [diff] [blame] | 32 | close(term_fd); |
Jeff King | 21aeafc | 2011-12-10 05:41:01 -0500 | [diff] [blame] | 33 | term_fd = -1; |
| 34 | } |
| 35 | |
Carlo Marcelo Arenas Belón | e22b245 | 2021-10-05 00:46:47 -0700 | [diff] [blame] | 36 | int save_term(int full_duplex) |
| 37 | { |
| 38 | if (term_fd < 0) |
| 39 | term_fd = open("/dev/tty", O_RDWR); |
| 40 | |
| 41 | return (term_fd < 0) ? -1 : tcgetattr(term_fd, &old_term); |
| 42 | } |
| 43 | |
Johannes Schindelin | 94ac3c3 | 2020-01-14 18:43:47 +0000 | [diff] [blame] | 44 | static int disable_bits(tcflag_t bits) |
Erik Faye-Lund | 9df92e6 | 2012-12-04 09:10:39 +0100 | [diff] [blame] | 45 | { |
| 46 | struct termios t; |
| 47 | |
Carlo Marcelo Arenas Belón | e22b245 | 2021-10-05 00:46:47 -0700 | [diff] [blame] | 48 | if (save_term(0) < 0) |
Erik Faye-Lund | 9df92e6 | 2012-12-04 09:10:39 +0100 | [diff] [blame] | 49 | goto error; |
| 50 | |
Carlo Marcelo Arenas Belón | e22b245 | 2021-10-05 00:46:47 -0700 | [diff] [blame] | 51 | t = old_term; |
Erik Faye-Lund | 9df92e6 | 2012-12-04 09:10:39 +0100 | [diff] [blame] | 52 | sigchain_push_common(restore_term_on_signal); |
| 53 | |
Johannes Schindelin | 94ac3c3 | 2020-01-14 18:43:47 +0000 | [diff] [blame] | 54 | t.c_lflag &= ~bits; |
Erik Faye-Lund | 9df92e6 | 2012-12-04 09:10:39 +0100 | [diff] [blame] | 55 | if (!tcsetattr(term_fd, TCSAFLUSH, &t)) |
| 56 | return 0; |
| 57 | |
| 58 | error: |
| 59 | close(term_fd); |
| 60 | term_fd = -1; |
| 61 | return -1; |
| 62 | } |
| 63 | |
Johannes Schindelin | 94ac3c3 | 2020-01-14 18:43:47 +0000 | [diff] [blame] | 64 | static int disable_echo(void) |
| 65 | { |
| 66 | return disable_bits(ECHO); |
| 67 | } |
| 68 | |
Johannes Schindelin | a5e46e6 | 2020-01-14 18:43:49 +0000 | [diff] [blame] | 69 | static int enable_non_canonical(void) |
| 70 | { |
| 71 | return disable_bits(ICANON | ECHO); |
| 72 | } |
| 73 | |
Jonathan Nieder | 380395d | 2013-05-02 20:26:08 +0100 | [diff] [blame] | 74 | #elif defined(GIT_WINDOWS_NATIVE) |
Erik Faye-Lund | afb4356 | 2012-12-04 09:10:41 +0100 | [diff] [blame] | 75 | |
| 76 | #define INPUT_PATH "CONIN$" |
| 77 | #define OUTPUT_PATH "CONOUT$" |
| 78 | #define FORCE_TEXT "t" |
| 79 | |
Johannes Schindelin | 9ea416c | 2020-01-14 18:43:48 +0000 | [diff] [blame] | 80 | static int use_stty = 1; |
| 81 | static struct string_list stty_restore = STRING_LIST_INIT_DUP; |
Erik Faye-Lund | afb4356 | 2012-12-04 09:10:41 +0100 | [diff] [blame] | 82 | static HANDLE hconin = INVALID_HANDLE_VALUE; |
Carlo Marcelo Arenas Belón | e22b245 | 2021-10-05 00:46:47 -0700 | [diff] [blame] | 83 | static HANDLE hconout = INVALID_HANDLE_VALUE; |
| 84 | static DWORD cmode_in, cmode_out; |
Erik Faye-Lund | afb4356 | 2012-12-04 09:10:41 +0100 | [diff] [blame] | 85 | |
Carlo Marcelo Arenas Belón | e22b245 | 2021-10-05 00:46:47 -0700 | [diff] [blame] | 86 | void restore_term(void) |
Erik Faye-Lund | afb4356 | 2012-12-04 09:10:41 +0100 | [diff] [blame] | 87 | { |
Johannes Schindelin | 9ea416c | 2020-01-14 18:43:48 +0000 | [diff] [blame] | 88 | if (use_stty) { |
| 89 | int i; |
| 90 | struct child_process cp = CHILD_PROCESS_INIT; |
| 91 | |
| 92 | if (stty_restore.nr == 0) |
| 93 | return; |
| 94 | |
Jeff King | ef8d7ac | 2020-07-28 16:24:53 -0400 | [diff] [blame] | 95 | strvec_push(&cp.args, "stty"); |
Johannes Schindelin | 9ea416c | 2020-01-14 18:43:48 +0000 | [diff] [blame] | 96 | for (i = 0; i < stty_restore.nr; i++) |
Jeff King | ef8d7ac | 2020-07-28 16:24:53 -0400 | [diff] [blame] | 97 | strvec_push(&cp.args, stty_restore.items[i].string); |
Johannes Schindelin | 9ea416c | 2020-01-14 18:43:48 +0000 | [diff] [blame] | 98 | run_command(&cp); |
| 99 | string_list_clear(&stty_restore, 0); |
| 100 | return; |
| 101 | } |
| 102 | |
Erik Faye-Lund | afb4356 | 2012-12-04 09:10:41 +0100 | [diff] [blame] | 103 | if (hconin == INVALID_HANDLE_VALUE) |
| 104 | return; |
| 105 | |
Carlo Marcelo Arenas Belón | e22b245 | 2021-10-05 00:46:47 -0700 | [diff] [blame] | 106 | SetConsoleMode(hconin, cmode_in); |
| 107 | CloseHandle(hconin); |
| 108 | if (cmode_out) { |
| 109 | assert(hconout != INVALID_HANDLE_VALUE); |
| 110 | SetConsoleMode(hconout, cmode_out); |
| 111 | CloseHandle(hconout); |
| 112 | } |
| 113 | |
| 114 | hconin = hconout = INVALID_HANDLE_VALUE; |
| 115 | } |
| 116 | |
| 117 | int save_term(int full_duplex) |
| 118 | { |
| 119 | hconin = CreateFileA("CONIN$", GENERIC_READ | GENERIC_WRITE, |
| 120 | FILE_SHARE_READ, NULL, OPEN_EXISTING, |
| 121 | FILE_ATTRIBUTE_NORMAL, NULL); |
| 122 | if (hconin == INVALID_HANDLE_VALUE) |
| 123 | return -1; |
| 124 | |
| 125 | if (full_duplex) { |
| 126 | hconout = CreateFileA("CONOUT$", GENERIC_READ | GENERIC_WRITE, |
| 127 | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, |
| 128 | FILE_ATTRIBUTE_NORMAL, NULL); |
| 129 | if (hconout == INVALID_HANDLE_VALUE) |
| 130 | goto error; |
| 131 | |
| 132 | GetConsoleMode(hconout, &cmode_out); |
| 133 | } |
| 134 | |
| 135 | GetConsoleMode(hconin, &cmode_in); |
| 136 | use_stty = 0; |
| 137 | return 0; |
| 138 | error: |
Erik Faye-Lund | afb4356 | 2012-12-04 09:10:41 +0100 | [diff] [blame] | 139 | CloseHandle(hconin); |
| 140 | hconin = INVALID_HANDLE_VALUE; |
Carlo Marcelo Arenas Belón | e22b245 | 2021-10-05 00:46:47 -0700 | [diff] [blame] | 141 | return -1; |
Erik Faye-Lund | afb4356 | 2012-12-04 09:10:41 +0100 | [diff] [blame] | 142 | } |
| 143 | |
Johannes Schindelin | 94ac3c3 | 2020-01-14 18:43:47 +0000 | [diff] [blame] | 144 | static int disable_bits(DWORD bits) |
Erik Faye-Lund | afb4356 | 2012-12-04 09:10:41 +0100 | [diff] [blame] | 145 | { |
Johannes Schindelin | 9ea416c | 2020-01-14 18:43:48 +0000 | [diff] [blame] | 146 | if (use_stty) { |
| 147 | struct child_process cp = CHILD_PROCESS_INIT; |
| 148 | |
Jeff King | ef8d7ac | 2020-07-28 16:24:53 -0400 | [diff] [blame] | 149 | strvec_push(&cp.args, "stty"); |
Johannes Schindelin | 9ea416c | 2020-01-14 18:43:48 +0000 | [diff] [blame] | 150 | |
| 151 | if (bits & ENABLE_LINE_INPUT) { |
| 152 | string_list_append(&stty_restore, "icanon"); |
Jeff King | ef8d7ac | 2020-07-28 16:24:53 -0400 | [diff] [blame] | 153 | strvec_push(&cp.args, "-icanon"); |
Johannes Schindelin | 9ea416c | 2020-01-14 18:43:48 +0000 | [diff] [blame] | 154 | } |
| 155 | |
| 156 | if (bits & ENABLE_ECHO_INPUT) { |
| 157 | string_list_append(&stty_restore, "echo"); |
Jeff King | ef8d7ac | 2020-07-28 16:24:53 -0400 | [diff] [blame] | 158 | strvec_push(&cp.args, "-echo"); |
Johannes Schindelin | 9ea416c | 2020-01-14 18:43:48 +0000 | [diff] [blame] | 159 | } |
| 160 | |
| 161 | if (bits & ENABLE_PROCESSED_INPUT) { |
| 162 | string_list_append(&stty_restore, "-ignbrk"); |
| 163 | string_list_append(&stty_restore, "intr"); |
| 164 | string_list_append(&stty_restore, "^c"); |
Jeff King | ef8d7ac | 2020-07-28 16:24:53 -0400 | [diff] [blame] | 165 | strvec_push(&cp.args, "ignbrk"); |
| 166 | strvec_push(&cp.args, "intr"); |
| 167 | strvec_push(&cp.args, ""); |
Johannes Schindelin | 9ea416c | 2020-01-14 18:43:48 +0000 | [diff] [blame] | 168 | } |
| 169 | |
| 170 | if (run_command(&cp) == 0) |
| 171 | return 0; |
| 172 | |
| 173 | /* `stty` could not be executed; access the Console directly */ |
| 174 | use_stty = 0; |
| 175 | } |
| 176 | |
Carlo Marcelo Arenas Belón | e22b245 | 2021-10-05 00:46:47 -0700 | [diff] [blame] | 177 | if (save_term(0) < 0) |
Erik Faye-Lund | afb4356 | 2012-12-04 09:10:41 +0100 | [diff] [blame] | 178 | return -1; |
| 179 | |
Erik Faye-Lund | afb4356 | 2012-12-04 09:10:41 +0100 | [diff] [blame] | 180 | sigchain_push_common(restore_term_on_signal); |
Carlo Marcelo Arenas Belón | e22b245 | 2021-10-05 00:46:47 -0700 | [diff] [blame] | 181 | if (!SetConsoleMode(hconin, cmode_in & ~bits)) { |
Erik Faye-Lund | afb4356 | 2012-12-04 09:10:41 +0100 | [diff] [blame] | 182 | CloseHandle(hconin); |
| 183 | hconin = INVALID_HANDLE_VALUE; |
| 184 | return -1; |
| 185 | } |
| 186 | |
| 187 | return 0; |
| 188 | } |
| 189 | |
Johannes Schindelin | 94ac3c3 | 2020-01-14 18:43:47 +0000 | [diff] [blame] | 190 | static int disable_echo(void) |
| 191 | { |
| 192 | return disable_bits(ENABLE_ECHO_INPUT); |
| 193 | } |
| 194 | |
Johannes Schindelin | a5e46e6 | 2020-01-14 18:43:49 +0000 | [diff] [blame] | 195 | static int enable_non_canonical(void) |
| 196 | { |
| 197 | return disable_bits(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT); |
| 198 | } |
Johannes Schindelin | 94ac3c3 | 2020-01-14 18:43:47 +0000 | [diff] [blame] | 199 | |
Johannes Schindelin | e118f06 | 2020-01-14 18:43:51 +0000 | [diff] [blame] | 200 | /* |
| 201 | * Override `getchar()`, as the default implementation does not use |
| 202 | * `ReadFile()`. |
| 203 | * |
| 204 | * This poses a problem when we want to see whether the standard |
| 205 | * input has more characters, as the default of Git for Windows is to start the |
| 206 | * Bash in a MinTTY, which uses a named pipe to emulate a pty, in which case |
| 207 | * our `poll()` emulation calls `PeekNamedPipe()`, which seems to require |
| 208 | * `ReadFile()` to be called first to work properly (it only reports 0 |
| 209 | * available bytes, otherwise). |
| 210 | * |
| 211 | * So let's just override `getchar()` with a version backed by `ReadFile()` and |
| 212 | * go our merry ways from here. |
| 213 | */ |
| 214 | static int mingw_getchar(void) |
| 215 | { |
| 216 | DWORD read = 0; |
| 217 | unsigned char ch; |
| 218 | |
| 219 | if (!ReadFile(GetStdHandle(STD_INPUT_HANDLE), &ch, 1, &read, NULL)) |
| 220 | return EOF; |
| 221 | |
| 222 | if (!read) { |
| 223 | error("Unexpected 0 read"); |
| 224 | return EOF; |
| 225 | } |
| 226 | |
| 227 | return ch; |
| 228 | } |
| 229 | #define getchar mingw_getchar |
| 230 | |
Erik Faye-Lund | afb4356 | 2012-12-04 09:10:41 +0100 | [diff] [blame] | 231 | #endif |
| 232 | |
| 233 | #ifndef FORCE_TEXT |
| 234 | #define FORCE_TEXT |
| 235 | #endif |
| 236 | |
Jeff King | 21aeafc | 2011-12-10 05:41:01 -0500 | [diff] [blame] | 237 | char *git_terminal_prompt(const char *prompt, int echo) |
| 238 | { |
| 239 | static struct strbuf buf = STRBUF_INIT; |
| 240 | int r; |
Erik Faye-Lund | 67fe735 | 2012-12-04 09:10:40 +0100 | [diff] [blame] | 241 | FILE *input_fh, *output_fh; |
Jeff King | 21aeafc | 2011-12-10 05:41:01 -0500 | [diff] [blame] | 242 | |
Erik Faye-Lund | afb4356 | 2012-12-04 09:10:41 +0100 | [diff] [blame] | 243 | input_fh = fopen(INPUT_PATH, "r" FORCE_TEXT); |
Erik Faye-Lund | 67fe735 | 2012-12-04 09:10:40 +0100 | [diff] [blame] | 244 | if (!input_fh) |
Jeff King | 21aeafc | 2011-12-10 05:41:01 -0500 | [diff] [blame] | 245 | return NULL; |
| 246 | |
Erik Faye-Lund | afb4356 | 2012-12-04 09:10:41 +0100 | [diff] [blame] | 247 | output_fh = fopen(OUTPUT_PATH, "w" FORCE_TEXT); |
Erik Faye-Lund | 67fe735 | 2012-12-04 09:10:40 +0100 | [diff] [blame] | 248 | if (!output_fh) { |
| 249 | fclose(input_fh); |
Erik Faye-Lund | 9df92e6 | 2012-12-04 09:10:39 +0100 | [diff] [blame] | 250 | return NULL; |
Jeff King | 21aeafc | 2011-12-10 05:41:01 -0500 | [diff] [blame] | 251 | } |
| 252 | |
Erik Faye-Lund | 67fe735 | 2012-12-04 09:10:40 +0100 | [diff] [blame] | 253 | if (!echo && disable_echo()) { |
| 254 | fclose(input_fh); |
| 255 | fclose(output_fh); |
| 256 | return NULL; |
| 257 | } |
Jeff King | 21aeafc | 2011-12-10 05:41:01 -0500 | [diff] [blame] | 258 | |
Erik Faye-Lund | 67fe735 | 2012-12-04 09:10:40 +0100 | [diff] [blame] | 259 | fputs(prompt, output_fh); |
| 260 | fflush(output_fh); |
| 261 | |
Junio C Hamano | 8f309ae | 2016-01-13 15:31:17 -0800 | [diff] [blame] | 262 | r = strbuf_getline_lf(&buf, input_fh); |
Jeff King | 21aeafc | 2011-12-10 05:41:01 -0500 | [diff] [blame] | 263 | if (!echo) { |
Erik Faye-Lund | 67fe735 | 2012-12-04 09:10:40 +0100 | [diff] [blame] | 264 | putc('\n', output_fh); |
| 265 | fflush(output_fh); |
Jeff King | 21aeafc | 2011-12-10 05:41:01 -0500 | [diff] [blame] | 266 | } |
| 267 | |
| 268 | restore_term(); |
Erik Faye-Lund | 67fe735 | 2012-12-04 09:10:40 +0100 | [diff] [blame] | 269 | fclose(input_fh); |
| 270 | fclose(output_fh); |
Jeff King | 21aeafc | 2011-12-10 05:41:01 -0500 | [diff] [blame] | 271 | |
| 272 | if (r == EOF) |
| 273 | return NULL; |
| 274 | return buf.buf; |
| 275 | } |
| 276 | |
Johannes Schindelin | 12acdf5 | 2020-01-14 18:43:52 +0000 | [diff] [blame] | 277 | /* |
| 278 | * The `is_known_escape_sequence()` function returns 1 if the passed string |
| 279 | * corresponds to an Escape sequence that the terminal capabilities contains. |
| 280 | * |
| 281 | * To avoid depending on ncurses or other platform-specific libraries, we rely |
| 282 | * on the presence of the `infocmp` executable to do the job for us (failing |
| 283 | * silently if the program is not available or refused to run). |
| 284 | */ |
| 285 | struct escape_sequence_entry { |
| 286 | struct hashmap_entry entry; |
| 287 | char sequence[FLEX_ARRAY]; |
| 288 | }; |
| 289 | |
| 290 | static int sequence_entry_cmp(const void *hashmap_cmp_fn_data, |
| 291 | const struct escape_sequence_entry *e1, |
| 292 | const struct escape_sequence_entry *e2, |
| 293 | const void *keydata) |
| 294 | { |
| 295 | return strcmp(e1->sequence, keydata ? keydata : e2->sequence); |
| 296 | } |
| 297 | |
| 298 | static int is_known_escape_sequence(const char *sequence) |
| 299 | { |
| 300 | static struct hashmap sequences; |
| 301 | static int initialized; |
| 302 | |
| 303 | if (!initialized) { |
| 304 | struct child_process cp = CHILD_PROCESS_INIT; |
| 305 | struct strbuf buf = STRBUF_INIT; |
| 306 | char *p, *eol; |
| 307 | |
| 308 | hashmap_init(&sequences, (hashmap_cmp_fn)sequence_entry_cmp, |
| 309 | NULL, 0); |
| 310 | |
Jeff King | ef8d7ac | 2020-07-28 16:24:53 -0400 | [diff] [blame] | 311 | strvec_pushl(&cp.args, "infocmp", "-L", "-1", NULL); |
Johannes Schindelin | 12acdf5 | 2020-01-14 18:43:52 +0000 | [diff] [blame] | 312 | if (pipe_command(&cp, NULL, 0, &buf, 0, NULL, 0)) |
| 313 | strbuf_setlen(&buf, 0); |
| 314 | |
| 315 | for (eol = p = buf.buf; *p; p = eol + 1) { |
| 316 | p = strchr(p, '='); |
| 317 | if (!p) |
| 318 | break; |
| 319 | p++; |
| 320 | eol = strchrnul(p, '\n'); |
| 321 | |
| 322 | if (starts_with(p, "\\E")) { |
| 323 | char *comma = memchr(p, ',', eol - p); |
| 324 | struct escape_sequence_entry *e; |
| 325 | |
| 326 | p[0] = '^'; |
| 327 | p[1] = '['; |
| 328 | FLEX_ALLOC_MEM(e, sequence, p, comma - p); |
| 329 | hashmap_entry_init(&e->entry, |
| 330 | strhash(e->sequence)); |
| 331 | hashmap_add(&sequences, &e->entry); |
| 332 | } |
| 333 | if (!*eol) |
| 334 | break; |
| 335 | } |
| 336 | initialized = 1; |
| 337 | } |
| 338 | |
| 339 | return !!hashmap_get_from_hash(&sequences, strhash(sequence), sequence); |
| 340 | } |
| 341 | |
Johannes Schindelin | a5e46e6 | 2020-01-14 18:43:49 +0000 | [diff] [blame] | 342 | int read_key_without_echo(struct strbuf *buf) |
| 343 | { |
| 344 | static int warning_displayed; |
| 345 | int ch; |
| 346 | |
| 347 | if (warning_displayed || enable_non_canonical() < 0) { |
| 348 | if (!warning_displayed) { |
| 349 | warning("reading single keystrokes not supported on " |
| 350 | "this platform; reading line instead"); |
| 351 | warning_displayed = 1; |
| 352 | } |
| 353 | |
| 354 | return strbuf_getline(buf, stdin); |
| 355 | } |
| 356 | |
| 357 | strbuf_reset(buf); |
| 358 | ch = getchar(); |
| 359 | if (ch == EOF) { |
| 360 | restore_term(); |
| 361 | return EOF; |
| 362 | } |
Johannes Schindelin | a5e46e6 | 2020-01-14 18:43:49 +0000 | [diff] [blame] | 363 | strbuf_addch(buf, ch); |
Johannes Schindelin | e118f06 | 2020-01-14 18:43:51 +0000 | [diff] [blame] | 364 | |
| 365 | if (ch == '\033' /* ESC */) { |
| 366 | /* |
| 367 | * We are most likely looking at an Escape sequence. Let's try |
| 368 | * to read more bytes, waiting at most half a second, assuming |
| 369 | * that the sequence is complete if we did not receive any byte |
| 370 | * within that time. |
| 371 | * |
| 372 | * Start by replacing the Escape byte with ^[ */ |
| 373 | strbuf_splice(buf, buf->len - 1, 1, "^[", 2); |
| 374 | |
Johannes Schindelin | 12acdf5 | 2020-01-14 18:43:52 +0000 | [diff] [blame] | 375 | /* |
| 376 | * Query the terminal capabilities once about all the Escape |
| 377 | * sequences it knows about, so that we can avoid waiting for |
| 378 | * half a second when we know that the sequence is complete. |
| 379 | */ |
| 380 | while (!is_known_escape_sequence(buf->buf)) { |
Johannes Schindelin | e118f06 | 2020-01-14 18:43:51 +0000 | [diff] [blame] | 381 | struct pollfd pfd = { .fd = 0, .events = POLLIN }; |
| 382 | |
| 383 | if (poll(&pfd, 1, 500) < 1) |
| 384 | break; |
| 385 | |
| 386 | ch = getchar(); |
| 387 | if (ch == EOF) |
| 388 | return 0; |
| 389 | strbuf_addch(buf, ch); |
| 390 | } |
| 391 | } |
| 392 | |
Johannes Schindelin | a5e46e6 | 2020-01-14 18:43:49 +0000 | [diff] [blame] | 393 | restore_term(); |
| 394 | return 0; |
| 395 | } |
| 396 | |
Jeff King | 21aeafc | 2011-12-10 05:41:01 -0500 | [diff] [blame] | 397 | #else |
| 398 | |
Carlo Marcelo Arenas Belón | e22b245 | 2021-10-05 00:46:47 -0700 | [diff] [blame] | 399 | int save_term(int full_duplex) |
| 400 | { |
| 401 | /* full_duplex == 1, but no support available */ |
| 402 | return -full_duplex; |
| 403 | } |
| 404 | |
| 405 | void restore_term(void) |
| 406 | { |
| 407 | } |
| 408 | |
Jeff King | 21aeafc | 2011-12-10 05:41:01 -0500 | [diff] [blame] | 409 | char *git_terminal_prompt(const char *prompt, int echo) |
| 410 | { |
| 411 | return getpass(prompt); |
| 412 | } |
| 413 | |
Johannes Schindelin | a5e46e6 | 2020-01-14 18:43:49 +0000 | [diff] [blame] | 414 | int read_key_without_echo(struct strbuf *buf) |
| 415 | { |
| 416 | static int warning_displayed; |
| 417 | const char *res; |
| 418 | |
| 419 | if (!warning_displayed) { |
| 420 | warning("reading single keystrokes not supported on this " |
| 421 | "platform; reading line instead"); |
| 422 | warning_displayed = 1; |
| 423 | } |
| 424 | |
| 425 | res = getpass(""); |
| 426 | strbuf_reset(buf); |
| 427 | if (!res) |
| 428 | return EOF; |
| 429 | strbuf_addstr(buf, res); |
| 430 | return 0; |
| 431 | } |
| 432 | |
Jeff King | 21aeafc | 2011-12-10 05:41:01 -0500 | [diff] [blame] | 433 | #endif |