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
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
17#ifdef HAVE_DEV_TTY
18
19#define INPUT_PATH "/dev/tty"
20#define OUTPUT_PATH "/dev/tty"
21
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);
31 close(term_fd);
32 term_fd = -1;
33}
34
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
56#elif defined(GIT_WINDOWS_NATIVE)
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
100char *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(&buf, input_fh, '\n');
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
142char *git_terminal_prompt(const char *prompt, int echo)
143{
144 return getpass(prompt);
145}
146
147#endif