*** empty log message ***
[gnupg.git] / util / ttyio.c
1 /* ttyio.c -  tty i/O functions
2  *      Copyright (C) 1998 Free Software Foundation, Inc.
3  *
4  * This file is part of GNUPG.
5  *
6  * GNUPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GNUPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdarg.h>
26 #include <unistd.h>
27 #ifdef HAVE_TCGETATTR
28   #include <termios.h>
29 #endif
30 #ifdef __MINGW32__ /* use the odd Win32 functions */
31   #include <windows.h>
32   #ifdef HAVE_TCGETATTR
33      #error mingw32 and termios
34   #endif
35 #endif
36 #include <errno.h>
37 #include <ctype.h>
38 #include "util.h"
39 #include "memory.h"
40 #include "ttyio.h"
41
42 #define CONTROL_D ('D' - 'A' + 1)
43
44
45 #ifdef __MINGW32__ /* use the odd Win32 functions */
46 static struct {
47     HANDLE in, out;
48 } con;
49 #define DEF_INPMODE  (ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT    \
50                                         |ENABLE_PROCESSED_INPUT )
51 #define HID_INPMODE  (ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT )
52 #define DEF_OUTMODE  (ENABLE_WRAP_AT_EOL_OUTPUT|ENABLE_PROCESSED_OUTPUT)
53
54 #else /* yeah, we have a real OS */
55 static FILE *ttyfp = NULL;
56 #endif
57
58 static int initialized;
59 static int last_prompt_len;
60
61 #ifdef HAVE_TCGETATTR
62 static struct termios termsave;
63 static int restore_termios;
64 #endif
65
66 #ifdef HAVE_TCGETATTR
67 static void
68 cleanup(void)
69 {
70     if( restore_termios ) {
71         restore_termios = 0; /* do it prios in case it is interrupted again */
72         if( tcsetattr(fileno(ttyfp), TCSAFLUSH, &termsave) )
73             log_error("tcsetattr() failed: %s\n", strerror(errno) );
74     }
75 }
76 #endif
77
78 static void
79 init_ttyfp()
80 {
81     if( initialized )
82         return;
83
84   #if defined(__MINGW32__)
85     {
86         SECURITY_ATTRIBUTES sa;
87
88         memset(&sa, 0, sizeof(sa));
89         sa.nLength = sizeof(sa);
90         sa.bInheritHandle = TRUE;
91         con.out = CreateFileA( "CONOUT$", GENERIC_READ|GENERIC_WRITE,
92                                FILE_SHARE_READ|FILE_SHARE_WRITE,
93                                &sa, OPEN_EXISTING, 0, 0 );
94         if( con.out == INVALID_HANDLE_VALUE )
95             log_fatal("open(CONOUT$) failed: rc=%d", (int)GetLastError() );
96         memset(&sa, 0, sizeof(sa));
97         sa.nLength = sizeof(sa);
98         sa.bInheritHandle = TRUE;
99         con.in = CreateFileA( "CONIN$", GENERIC_READ|GENERIC_WRITE,
100                                FILE_SHARE_READ|FILE_SHARE_WRITE,
101                                &sa, OPEN_EXISTING, 0, 0 );
102         if( con.in == INVALID_HANDLE_VALUE )
103             log_fatal("open(CONIN$) failed: rc=%d", (int)GetLastError() );
104     }
105     SetConsoleMode(con.in, DEF_INPMODE );
106     SetConsoleMode(con.out, DEF_OUTMODE );
107
108   #else
109     ttyfp = fopen("/dev/tty", "r+");
110     if( !ttyfp )
111         log_fatal("cannot open /dev/tty: %s\n", strerror(errno) );
112   #endif
113   #ifdef HAVE_TCGETATTR
114     atexit( cleanup );
115   #endif
116     initialized = 1;
117 }
118
119
120 void
121 tty_printf( const char *fmt, ... )
122 {
123     va_list arg_ptr;
124
125     if( !initialized )
126         init_ttyfp();
127
128     va_start( arg_ptr, fmt ) ;
129   #ifdef __MINGW32__
130     { static char *buf;
131       static size_t bufsize;
132         int n;
133         DWORD nwritten;
134
135       #if 0 /* the dox say, that there is a snprintf, but I didn't found
136              * it, so we use a static buffer for now */
137         do {
138             if( n == -1 || !buf ) {
139                 m_free(buf);
140                 bufsize += 200;
141                 /* better check the new size; (we use M$ functions) */
142                 if( bufsize > 50000 )
143                     log_bug("vsnprintf probably failed\n");
144                 buf = m_alloc( bufsize );
145             }
146             n = _vsnprintf(buf, bufsize-1, fmt, arg_ptr);
147         } while( n == -1 );
148       #else
149         if( !buf ) {
150             bufsize += 1000;
151             buf = m_alloc( bufsize );
152         }
153         n = vsprintf(buf, fmt, arg_ptr);
154         if( n == -1 )
155             log_bug("vsprintf() failed\n");
156       #endif
157
158         if( !WriteConsoleA( con.out, buf, n, &nwritten, NULL ) )
159             log_fatal("WriteConsole failed: rc=%d", (int)GetLastError() );
160         if( n != nwritten )
161             log_fatal("WriteConsole failed: %d != %ld\n", n, nwritten );
162         last_prompt_len += n;
163     }
164   #else
165     last_prompt_len += vfprintf(ttyfp,fmt,arg_ptr) ;
166     fflush(ttyfp);
167   #endif
168     va_end(arg_ptr);
169 }
170
171
172 /****************
173  * Print a string, but filter all control characters out.
174  */
175 void
176 tty_print_string( byte *p, size_t n )
177 {
178     if( !initialized )
179         init_ttyfp();
180
181   #ifdef __MINGW32__
182     /* not so effective, change it if you want */
183     for( ; n; n--, p++ )
184         if( iscntrl( *p ) ) {
185             if( *p == '\n' )
186                 tty_printf("\\n");
187             else if( !*p )
188                 tty_printf("\\0");
189             else
190                 tty_printf("\\x%02x", *p);
191         }
192         else
193             tty_printf("%c", *p);
194   #else
195     for( ; n; n--, p++ )
196         if( iscntrl( *p ) ) {
197             putc('\\', ttyfp);
198             if( *p == '\n' )
199                 putc('n', ttyfp);
200             else if( !*p )
201                 putc('0', ttyfp);
202             else
203                 fprintf(ttyfp, "x%02x", *p );
204         }
205         else
206             putc(*p, ttyfp);
207   #endif
208 }
209
210
211
212
213
214 static char *
215 do_get( const char *prompt, int hidden )
216 {
217     char *buf;
218     byte cbuf[1];
219     int c, n, i;
220
221     if( !initialized )
222         init_ttyfp();
223
224     last_prompt_len = 0;
225     tty_printf( prompt );
226     buf = m_alloc(n=50);
227     i = 0;
228
229   #if __MINGW32__ /* windoze version */
230     if( hidden )
231         SetConsoleMode(con.in, HID_INPMODE );
232
233     for(;;) {
234         DWORD nread;
235
236         if( !ReadConsoleA( con.in, cbuf, 1, &nread, NULL ) )
237             log_fatal("ReadConsole failed: rc=%d", (int)GetLastError() );
238         if( !nread )
239             continue;
240         if( *cbuf == '\n' )
241             break;
242
243         if( !hidden )
244             last_prompt_len++;
245         c = *cbuf;
246         if( c == '\t' )
247             c = ' ';
248         else if( c > 0xa0 )
249             ; /* we don't allow 0xa0, as this is a protected blank which may
250                * confuse the user */
251         else if( iscntrl(c) )
252             continue;
253         if( !(i < n-1) ) {
254             n += 50;
255             buf = m_realloc( buf, n );
256         }
257         buf[i++] = c;
258     }
259
260     if( hidden )
261         SetConsoleMode(con.in, DEF_INPMODE );
262
263   #else /* unix version */
264     if( hidden ) {
265       #ifdef HAVE_TCGETATTR
266         struct termios term;
267
268         if( tcgetattr(fileno(ttyfp), &termsave) )
269             log_fatal("tcgetattr() failed: %s\n", strerror(errno) );
270         restore_termios = 1;
271         term = termsave;
272         term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
273         if( tcsetattr( fileno(ttyfp), TCSAFLUSH, &term ) )
274             log_fatal("tcsetattr() failed: %s\n", strerror(errno) );
275       #endif
276     }
277
278     /* fixme: How can we avoid that the \n is echoed w/o disabling
279      * canonical mode - w/o this kill_prompt can't work */
280     while( read(fileno(ttyfp), cbuf, 1) == 1 && *cbuf != '\n' ) {
281         if( !hidden )
282             last_prompt_len++;
283         c = *cbuf;
284         if( c == CONTROL_D )
285             log_info("control d found\n");
286         if( c == '\t' )
287             c = ' ';
288         else if( c > 0xa0 )
289             ; /* we don't allow 0xa0, as this is a protected blank which may
290                * confuse the user */
291         else if( iscntrl(c) )
292             continue;
293         if( !(i < n-1) ) {
294             n += 50;
295             buf = m_realloc( buf, n );
296         }
297         buf[i++] = c;
298     }
299     if( *cbuf != '\n' ) {
300         buf[0] = CONTROL_D;
301         i = 1;
302     }
303
304
305     if( hidden ) {
306       #ifdef HAVE_TCGETATTR
307         if( tcsetattr(fileno(ttyfp), TCSAFLUSH, &termsave) )
308             log_error("tcsetattr() failed: %s\n", strerror(errno) );
309         restore_termios = 0;
310       #endif
311     }
312   #endif /* end unix version */
313     buf[i] = 0;
314     return buf;
315 }
316
317
318 char *
319 tty_get( const char *prompt )
320 {
321     return do_get( prompt, 0 );
322 }
323
324 char *
325 tty_get_hidden( const char *prompt )
326 {
327     return do_get( prompt, 1 );
328 }
329
330
331 void
332 tty_kill_prompt()
333 {
334
335     if( !initialized )
336         init_ttyfp();
337     if( !last_prompt_len )
338         return;
339   #if __MINGW32__
340     tty_printf("\r%*s\r", last_prompt_len, "");
341   #else
342     {
343         int i;
344         putc('\r', ttyfp);
345         for(i=0; i < last_prompt_len; i ++ )
346             putc(' ', ttyfp);
347         putc('\r', ttyfp);
348         fflush(ttyfp);
349     }
350   #endif
351     last_prompt_len = 0;
352 }
353
354
355 int
356 tty_get_answer_is_yes( const char *prompt )
357 {
358     int yes;
359     char *p = tty_get( prompt );
360     tty_kill_prompt();
361     yes = answer_is_yes(p);
362     m_free(p);
363     return yes;
364 }
365