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 <stdarg.h>
19 #include "sysincludes.h"
20 #include "mtools.h"
21 
22 static FILE *tty=NULL;
23 static int notty=0;
24 static int ttyfd=-1;
25 #ifdef USE_RAWTERM
26 int	mtools_raw_tty = 1;
27 #else
28 int	mtools_raw_tty = 0;
29 #endif
30 
31 #ifdef USE_RAWTERM
32 # if defined TCSANOW && defined HAVE_TCSETATTR
33 /* we have tcsetattr & tcgetattr. Good */
34 typedef struct termios Terminal;
35 #  define stty(a,b)        (void)tcsetattr(a,TCSANOW,b)
36 #  define gtty(a,b)        (void)tcgetattr(a,b)
37 #  define USE_TCIFLUSH
38 
39 # elif defined TCSETS && defined TCGETS
40 typedef struct termios Terminal;
41 #  define stty(a,b) (void)ioctl(a,TCSETS,(char *)b)
42 #  define gtty(a,b) (void)ioctl(a,TCGETS,(char *)b)
43 #  define USE_TCIFLUSH
44 
45 # elif defined TCSETA && defined TCGETA
46 typedef struct termio Terminal;
47 #  define stty(a,b) (void)ioctl(a,TCSETA,(char *)b)
48 #  define gtty(a,b) (void)ioctl(a,TCGETA,(char *)b)
49 #  define USE_TCIFLUSH
50 
51 # elif defined(HAVE_SGTTY_H) && defined(TIOCSETP) && defined(TIOCGETP)
52 typedef struct sgttyb Terminal;
53 #  define stty(a,b) (void)ioctl(a,TIOCSETP,(char *)b)
54 #  define gtty(a,b) (void)ioctl(a,TIOCGETP,(char *)b)
55 #  define USE_SGTTY
56 #  define discard_input(a) /**/
57 
58 # else
59 /* no way to use raw terminal */
60 #  warning Cannot use raw terminal code (disabled)
61 #  undef USE_RAWTERM
62 # endif
63 
64 #endif
65 
66 #ifdef USE_TCIFLUSH
67 # if defined TCIFLUSH && defined HAVE_TCFLUSH
68 #  define discard_input(a) tcflush(a,TCIFLUSH)
69 # else
70 #  define discard_input(a) /**/
71 # endif
72 #endif
73 
74 #ifdef USE_RAWTERM
75 
76 static int tty_mode = -1; /* 1 for raw, 0 for cooked, -1 for initial */
77 static int need_tty_reset = 0;
78 static int handlerIsSet = 0;
79 
80 #define restore_tty(a) stty(STDIN,a)
81 
82 
83 #define STDIN ttyfd
84 #ifdef future
85 #define FAIL (-1)
86 #endif
87 #define DONE 0
88 static Terminal in_orig;
89 
90 /*--------------- Signal Handler routines -------------*/
91 
92 static void tty_time_out(int dummy UNUSEDP) NORETURN;
tty_time_out(int dummy UNUSEDP)93 static void tty_time_out(int dummy UNUSEDP)
94 {
95 	int exit_code;
96 	signal(SIGALRM, SIG_IGN);
97 	if(tty && need_tty_reset)
98 		restore_tty (&in_orig);
99 #ifdef future
100 	if (fail_on_timeout)
101 		exit_code=SHFAIL;
102 	else {
103 		if (default_choice && mode_defined) {
104 			if (yes_no) {
105 				if ('Y' == default_choice)
106 					exit_code=0;
107 				else
108 					exit_code=1;
109 			} else
110 				exit_code=default_choice-minc+1;
111 		} else
112 			exit_code=DONE;
113 	}
114 #else
115 	exit_code = DONE;
116 #endif
117 	exit(exit_code);
118 }
119 
cleanup_tty(void)120 static void cleanup_tty(void)
121 {
122 	if(tty && need_tty_reset) {
123 		restore_tty (&in_orig);
124 		setup_signal();
125 	}
126 }
127 
set_raw_tty(int mode)128 static void set_raw_tty(int mode)
129 {
130 	Terminal in_raw;
131 
132 	if(mode != tty_mode && mode != -1) {
133 		if(!handlerIsSet) {
134 			/* Determine existing TTY settings */
135 			gtty (STDIN, &in_orig);
136 			need_tty_reset = 1;
137 
138 			/* Restore original TTY settings on exit */
139 			atexit(cleanup_tty);
140 			handlerIsSet = 1;
141 		}
142 
143 
144 		setup_signal();
145 		signal (SIGALRM, tty_time_out);
146 
147 		/* Change STDIN settings to raw */
148 
149 		gtty (STDIN, &in_raw);
150 		if(mode) {
151 #ifdef USE_SGTTY
152 			in_raw.sg_flags |= CBREAK;
153 #else
154 			in_raw.c_lflag &= ~ICANON;
155 			in_raw.c_cc[VMIN]=1;
156 			in_raw.c_cc[VTIME]=0;
157 #endif
158 			stty (STDIN, &in_raw);
159 		} else {
160 #ifdef USE_SGTTY
161 			in_raw.sg_flags &= ~CBREAK;
162 #else
163 			in_raw.c_lflag |= ICANON;
164 #endif
165 			stty (STDIN, &in_raw);
166 		}
167 		tty_mode = mode;
168 		discard_input(STDIN);
169 	}
170 }
171 #endif
172 
opentty(int mode)173 FILE *opentty(int mode)
174 {
175 	if(notty)
176 		return NULL;
177 	if (tty == NULL) {
178 		ttyfd = open("/dev/tty", O_RDONLY);
179 		if(ttyfd >= 0) {
180 			tty = fdopen(ttyfd, "r");
181 		}
182 	}
183 	if  (tty == NULL){
184 		if ( !isatty(0) ){
185 			notty = 1;
186 			return NULL;
187 		}
188 		ttyfd = 0;
189 		tty = stdin;
190 	}
191 #ifdef USE_RAWTERM
192 	if(mtools_raw_tty)
193 		set_raw_tty(mode);
194 #endif
195 	return tty;
196 }
197 
ask_confirmation(const char * format,...)198 int ask_confirmation(const char *format, ...)
199 {
200 	char ans[10];
201 	va_list ap;
202 
203 	if(!opentty(-1))
204 		return 0;
205 
206 	while (1) {
207 		va_start(ap, format);
208 		vfprintf(stderr, format, ap);
209 		va_end(ap);
210 		fflush(stderr);
211 		fflush(opentty(-1));
212 		if (mtools_raw_tty) {
213 			ans[0] = fgetc(opentty(1));
214 			fputs("\n", stderr);
215 		} else {
216 			if(fgets(ans,9, opentty(0)) == NULL)
217 				/* Treat end-of-file as no */
218 				ans[0] = 'n';
219 		}
220 		if (ans[0] == 'y' || ans[0] == 'Y')
221 			return 0;
222 		if (ans[0] == 'n' || ans[0] == 'N')
223 			return -1;
224 	}
225 }
226