]> Pileus Git - ~andy/git/blob - compat/terminal.c
compat/terminal: separate input and output handles
[~andy/git] / compat / terminal.c
1 #include "git-compat-util.h"
2 #include "compat/terminal.h"
3 #include "sigchain.h"
4 #include "strbuf.h"
5
6 #ifdef HAVE_DEV_TTY
7
8 static int term_fd = -1;
9 static struct termios old_term;
10
11 static void restore_term(void)
12 {
13         if (term_fd < 0)
14                 return;
15
16         tcsetattr(term_fd, TCSAFLUSH, &old_term);
17         close(term_fd);
18         term_fd = -1;
19 }
20
21 static void restore_term_on_signal(int sig)
22 {
23         restore_term();
24         sigchain_pop(sig);
25         raise(sig);
26 }
27
28 static int disable_echo(void)
29 {
30         struct termios t;
31
32         term_fd = open("/dev/tty", O_RDWR);
33         if (tcgetattr(term_fd, &t) < 0)
34                 goto error;
35
36         old_term = t;
37         sigchain_push_common(restore_term_on_signal);
38
39         t.c_lflag &= ~ECHO;
40         if (!tcsetattr(term_fd, TCSAFLUSH, &t))
41                 return 0;
42
43 error:
44         close(term_fd);
45         term_fd = -1;
46         return -1;
47 }
48
49 char *git_terminal_prompt(const char *prompt, int echo)
50 {
51         static struct strbuf buf = STRBUF_INIT;
52         int r;
53         FILE *input_fh, *output_fh;
54
55         input_fh = fopen("/dev/tty", "r");
56         if (!input_fh)
57                 return NULL;
58
59         output_fh = fopen("/dev/tty", "w");
60         if (!output_fh) {
61                 fclose(input_fh);
62                 return NULL;
63         }
64
65         if (!echo && disable_echo()) {
66                 fclose(input_fh);
67                 fclose(output_fh);
68                 return NULL;
69         }
70
71         fputs(prompt, output_fh);
72         fflush(output_fh);
73
74         r = strbuf_getline(&buf, input_fh, '\n');
75         if (!echo) {
76                 putc('\n', output_fh);
77                 fflush(output_fh);
78         }
79
80         restore_term();
81         fclose(input_fh);
82         fclose(output_fh);
83
84         if (r == EOF)
85                 return NULL;
86         return buf.buf;
87 }
88
89 #else
90
91 char *git_terminal_prompt(const char *prompt, int echo)
92 {
93         return getpass(prompt);
94 }
95
96 #endif