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 
8 static void restore_term(void);
9 
restore_term_on_signal(int sig)10 static 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 
22 static int term_fd = -1;
23 static struct termios old_term;
24 
restore_term(void)25 static 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 
disable_echo(void)35 static 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 
50 error:
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 
62 static HANDLE hconin = INVALID_HANDLE_VALUE;
63 static DWORD cmode;
64 
restore_term(void)65 static 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 
disable_echo(void)75 static 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 
git_terminal_prompt(const char * prompt,int echo)100 char *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_lf(&buf, input_fh);
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 
git_terminal_prompt(const char * prompt,int echo)142 char *git_terminal_prompt(const char *prompt, int echo)
143 {
144 	return getpass(prompt);
145 }
146 
147 #endif
148