1 /* Copyright 1997,2001,2002,2007-2009 Alain Knaff.
2 * This file is part of mtools.
3 *
4 * Mtools is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * Mtools is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with Mtools. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "sysincludes.h"
19 #include "mtools.h"
20
21 static FILE *tty=NULL;
22 static int notty=0;
23 static int ttyfd=-1;
24 #ifdef USE_RAWTERM
25 int mtools_raw_tty = 1;
26 #else
27 int mtools_raw_tty = 0;
28 #endif
29
30 #ifdef USE_RAWTERM
31 # if defined TCSANOW && defined HAVE_TCSETATTR
32 /* we have tcsetattr & tcgetattr. Good */
33 typedef struct termios Terminal;
34 # define stty(a,b) (void)tcsetattr(a,TCSANOW,b)
35 # define gtty(a,b) (void)tcgetattr(a,b)
36 # define USE_TCIFLUSH
37
38 # elif defined TCSETS && defined TCGETS
39 typedef struct termios Terminal;
40 # define stty(a,b) (void)ioctl(a,TCSETS,(char *)b)
41 # define gtty(a,b) (void)ioctl(a,TCGETS,(char *)b)
42 # define USE_TCIFLUSH
43
44 # elif defined TCSETA && defined TCGETA
45 typedef struct termio Terminal;
46 # define stty(a,b) (void)ioctl(a,TCSETA,(char *)b)
47 # define gtty(a,b) (void)ioctl(a,TCGETA,(char *)b)
48 # define USE_TCIFLUSH
49
50 # elif defined(HAVE_SGTTY_H) && defined(TIOCSETP) && defined(TIOCGETP)
51 typedef struct sgttyb Terminal;
52 # define stty(a,b) (void)ioctl(a,TIOCSETP,(char *)b)
53 # define gtty(a,b) (void)ioctl(a,TIOCGETP,(char *)b)
54 # define USE_SGTTY
55 # define discard_input(a) /**/
56
57 # else
58 /* no way to use raw terminal */
59 # warning Cannot use raw terminal code (disabled)
60 # undef USE_RAWTERM
61 # endif
62
63 #endif
64
65 #ifdef USE_TCIFLUSH
66 # if defined TCIFLUSH && defined HAVE_TCFLUSH
67 # define discard_input(a) tcflush(a,TCIFLUSH)
68 # else
69 # define discard_input(a) /**/
70 # endif
71 #endif
72
73 #ifdef USE_RAWTERM
74
75 static int tty_mode = -1; /* 1 for raw, 0 for cooked, -1 for initial */
76 static int need_tty_reset = 0;
77 static int handlerIsSet = 0;
78
79 #define restore_tty(a) stty(STDIN,a)
80
81
82 #define STDIN ttyfd
83 #ifdef future
84 #define FAIL (-1)
85 #endif
86 #define DONE 0
87 static Terminal in_orig;
88
89 /*--------------- Signal Handler routines -------------*/
90
91 static void tty_time_out(int dummy UNUSEDP) NORETURN;
tty_time_out(int dummy UNUSEDP)92 static void tty_time_out(int dummy UNUSEDP)
93 {
94 int exit_code;
95 signal(SIGALRM, SIG_IGN);
96 if(tty && need_tty_reset)
97 restore_tty (&in_orig);
98 #ifdef future
99 if (fail_on_timeout)
100 exit_code=SHFAIL;
101 else {
102 if (default_choice && mode_defined) {
103 if (yes_no) {
104 if ('Y' == default_choice)
105 exit_code=0;
106 else
107 exit_code=1;
108 } else
109 exit_code=default_choice-minc+1;
110 } else
111 exit_code=DONE;
112 }
113 #else
114 exit_code = DONE;
115 #endif
116 exit(exit_code);
117 }
118
cleanup_tty(void)119 static void cleanup_tty(void)
120 {
121 if(tty && need_tty_reset) {
122 restore_tty (&in_orig);
123 setup_signal();
124 }
125 }
126
set_raw_tty(int mode)127 static void set_raw_tty(int mode)
128 {
129 Terminal in_raw;
130
131 if(mode != tty_mode && mode != -1) {
132 if(!handlerIsSet) {
133 /* Determine existing TTY settings */
134 gtty (STDIN, &in_orig);
135 need_tty_reset = 1;
136
137 /* Restore original TTY settings on exit */
138 atexit(cleanup_tty);
139 handlerIsSet = 1;
140 }
141
142
143 setup_signal();
144 signal (SIGALRM, tty_time_out);
145
146 /* Change STDIN settings to raw */
147
148 gtty (STDIN, &in_raw);
149 if(mode) {
150 #ifdef USE_SGTTY
151 in_raw.sg_flags |= CBREAK;
152 #else
153 in_raw.c_lflag &= ~0u ^ ICANON;
154 in_raw.c_cc[VMIN]=1;
155 in_raw.c_cc[VTIME]=0;
156 #endif
157 stty (STDIN, &in_raw);
158 } else {
159 #ifdef USE_SGTTY
160 in_raw.sg_flags &= ~CBREAK;
161 #else
162 in_raw.c_lflag |= ICANON;
163 #endif
164 stty (STDIN, &in_raw);
165 }
166 tty_mode = mode;
167 discard_input(STDIN);
168 }
169 }
170 #endif
171
opentty(int mode UNUSEDP)172 FILE *opentty(int mode UNUSEDP)
173 {
174 if(notty)
175 return NULL;
176 if (tty == NULL) {
177 ttyfd = open("/dev/tty", O_RDONLY);
178 if(ttyfd >= 0) {
179 tty = fdopen(ttyfd, "r");
180 }
181 }
182 if (tty == NULL){
183 if ( !isatty(0) ){
184 notty = 1;
185 return NULL;
186 }
187 ttyfd = 0;
188 tty = stdin;
189 }
190 #ifdef USE_RAWTERM
191 if(mtools_raw_tty)
192 set_raw_tty(mode);
193 #endif
194 return tty;
195 }
196
ask_confirmation(const char * format,...)197 int ask_confirmation(const char *format, ...)
198 {
199 char ans[10];
200 va_list ap;
201
202 if(!opentty(-1))
203 return 0;
204
205 while (1) {
206 va_start(ap, format);
207 vfprintf(stderr, format, ap);
208 va_end(ap);
209 fflush(stderr);
210 fflush(opentty(-1));
211 if (mtools_raw_tty) {
212 int c = fgetc(opentty(1));
213 if(c < 0)
214 /* Treat end-of-file or error as no */
215 ans[0] = 'n';
216 else
217 ans[0] = (char) c;
218 fputs("\n", stderr);
219 } else {
220 if(fgets(ans,9, opentty(0)) == NULL)
221 /* Treat end-of-file as no */
222 ans[0] = 'n';
223 }
224 if (ans[0] == 'y' || ans[0] == 'Y')
225 return 0;
226 if (ans[0] == 'n' || ans[0] == 'N')
227 return -1;
228 }
229 }
230