Merge branch 'jk/perf-no-dups'
[git/git.git] / compat / terminal.c
CommitLineData
21aeafce
JK
1#include "git-compat-util.h"
2#include "compat/terminal.h"
3#include "sigchain.h"
4#include "strbuf.h"
5
380395d0 6#if defined(HAVE_DEV_TTY) || defined(GIT_WINDOWS_NATIVE)
afb43561
EFL
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
21aeafce
JK
17#ifdef HAVE_DEV_TTY
18
afb43561
EFL
19#define INPUT_PATH "/dev/tty"
20#define OUTPUT_PATH "/dev/tty"
21
21aeafce
JK
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);
9df92e63 31 close(term_fd);
21aeafce
JK
32 term_fd = -1;
33}
34
9df92e63
EFL
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
380395d0 56#elif defined(GIT_WINDOWS_NATIVE)
afb43561
EFL
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
21aeafce
JK
100char *git_terminal_prompt(const char *prompt, int echo)
101{
102 static struct strbuf buf = STRBUF_INIT;
103 int r;
67fe7356 104 FILE *input_fh, *output_fh;
21aeafce 105
afb43561 106 input_fh = fopen(INPUT_PATH, "r" FORCE_TEXT);
67fe7356 107 if (!input_fh)
21aeafce
JK
108 return NULL;
109
afb43561 110 output_fh = fopen(OUTPUT_PATH, "w" FORCE_TEXT);
67fe7356
EFL
111 if (!output_fh) {
112 fclose(input_fh);
113 return NULL;
114 }
115
9df92e63 116 if (!echo && disable_echo()) {
67fe7356
EFL
117 fclose(input_fh);
118 fclose(output_fh);
9df92e63 119 return NULL;
21aeafce
JK
120 }
121
67fe7356
EFL
122 fputs(prompt, output_fh);
123 fflush(output_fh);
21aeafce 124
8f309aeb 125 r = strbuf_getline_lf(&buf, input_fh);
21aeafce 126 if (!echo) {
67fe7356
EFL
127 putc('\n', output_fh);
128 fflush(output_fh);
21aeafce
JK
129 }
130
131 restore_term();
67fe7356
EFL
132 fclose(input_fh);
133 fclose(output_fh);
21aeafce
JK
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