See ChangeLog: Thu May 20 14:04:08 CEST 1999 Werner Koch
[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 static int batchmode;
61
62 #ifdef HAVE_TCGETATTR
63 static struct termios termsave;
64 static int restore_termios;
65 #endif
66
67 #ifdef HAVE_TCGETATTR
68 static void
69 cleanup(void)
70 {
71     if( restore_termios ) {
72         restore_termios = 0; /* do it prios in case it is interrupted again */
73         if( tcsetattr(fileno(ttyfp), TCSAFLUSH, &termsave) )
74             log_error("tcsetattr() failed: %s\n", strerror(errno) );
75     }
76 }
77 #endif
78
79 static void
80 init_ttyfp(void)
81 {
82     if( initialized )
83         return;
84
85   #if defined(__MINGW32__)
86     {
87         SECURITY_ATTRIBUTES sa;
88
89         memset(&sa, 0, sizeof(sa));
90         sa.nLength = sizeof(sa);
91         sa.bInheritHandle = TRUE;
92         con.out = CreateFileA( "CONOUT$", GENERIC_READ|GENERIC_WRITE,
93                                FILE_SHARE_READ|FILE_SHARE_WRITE,
94                                &sa, OPEN_EXISTING, 0, 0 );
95         if( con.out == INVALID_HANDLE_VALUE )
96             log_fatal("open(CONOUT$) failed: rc=%d", (int)GetLastError() );
97         memset(&sa, 0, sizeof(sa));
98         sa.nLength = sizeof(sa);
99         sa.bInheritHandle = TRUE;
100         con.in = CreateFileA( "CONIN$", GENERIC_READ|GENERIC_WRITE,
101                                FILE_SHARE_READ|FILE_SHARE_WRITE,
102                                &sa, OPEN_EXISTING, 0, 0 );
103         if( con.in == INVALID_HANDLE_VALUE )
104             log_fatal("open(CONIN$) failed: rc=%d", (int)GetLastError() );
105     }
106     SetConsoleMode(con.in, DEF_INPMODE );
107     SetConsoleMode(con.out, DEF_OUTMODE );
108
109   #elif defined(__EMX__)
110     ttyfp = stdout; /* Fixme: replace by the real functions: see wklib */
111   #else
112     ttyfp = batchmode? stderr : fopen("/dev/tty", "r+");
113     if( !ttyfp ) {
114         log_error("cannot open /dev/tty: %s\n", strerror(errno) );
115         exit(2);
116     }
117   #endif
118   #ifdef HAVE_TCGETATTR
119     atexit( cleanup );
120   #endif
121     initialized = 1;
122 }
123
124 int
125 tty_batchmode( int onoff )
126 {
127     int old = batchmode;
128     if( onoff != -1 )
129         batchmode = onoff;
130     return old;
131 }
132
133 void
134 tty_printf( const char *fmt, ... )
135 {
136     va_list arg_ptr;
137
138     if( !initialized )
139         init_ttyfp();
140
141     va_start( arg_ptr, fmt ) ;
142   #ifdef __MINGW32__
143     { static char *buf;
144       static size_t bufsize;
145         int n;
146         DWORD nwritten;
147
148       #if 0 /* the dox say, that there is a snprintf, but I didn't found
149              * it, so we use a static buffer for now */
150         do {
151             if( n == -1 || !buf ) {
152                 m_free(buf);
153                 bufsize += 200;
154                 /* better check the new size; (we use M$ functions) */
155                 if( bufsize > 50000 )
156                     log_bug("vsnprintf probably failed\n");
157                 buf = m_alloc( bufsize );
158             }
159             n = _vsnprintf(buf, bufsize-1, fmt, arg_ptr);
160         } while( n == -1 );
161       #else
162         if( !buf ) {
163             bufsize += 1000;
164             buf = m_alloc( bufsize );
165         }
166         n = vsprintf(buf, fmt, arg_ptr);
167         if( n == -1 )
168             log_bug("vsprintf() failed\n");
169       #endif
170
171         if( !WriteConsoleA( con.out, buf, n, &nwritten, NULL ) )
172             log_fatal("WriteConsole failed: rc=%d", (int)GetLastError() );
173         if( n != nwritten )
174             log_fatal("WriteConsole failed: %d != %ld\n", n, nwritten );
175         last_prompt_len += n;
176     }
177   #else
178     last_prompt_len += vfprintf(ttyfp,fmt,arg_ptr) ;
179     fflush(ttyfp);
180   #endif
181     va_end(arg_ptr);
182 }
183
184
185 /****************
186  * Print a string, but filter all control characters out.
187  */
188 void
189 tty_print_string( byte *p, size_t n )
190 {
191     if( !initialized )
192         init_ttyfp();
193
194   #ifdef __MINGW32__
195     /* not so effective, change it if you want */
196     for( ; n; n--, p++ )
197         if( iscntrl( *p ) ) {
198             if( *p == '\n' )
199                 tty_printf("\\n");
200             else if( !*p )
201                 tty_printf("\\0");
202             else
203                 tty_printf("\\x%02x", *p);
204         }
205         else
206             tty_printf("%c", *p);
207   #else
208     for( ; n; n--, p++ )
209         if( iscntrl( *p ) ) {
210             putc('\\', ttyfp);
211             if( *p == '\n' )
212                 putc('n', ttyfp);
213             else if( !*p )
214                 putc('0', ttyfp);
215             else
216                 fprintf(ttyfp, "x%02x", *p );
217         }
218         else
219             putc(*p, ttyfp);
220   #endif
221 }
222
223
224
225
226
227 static char *
228 do_get( const char *prompt, int hidden )
229 {
230     char *buf;
231     byte cbuf[1];
232     int c, n, i;
233
234     if( batchmode ) {
235         log_error("Sorry, we are in batchmode - can't get input\n");
236         exit(2);
237     }
238
239     if( !initialized )
240         init_ttyfp();
241
242     last_prompt_len = 0;
243     tty_printf( prompt );
244     buf = m_alloc(n=50);
245     i = 0;
246
247   #if __MINGW32__ /* windoze version */
248     if( hidden )
249         SetConsoleMode(con.in, HID_INPMODE );
250
251     for(;;) {
252         DWORD nread;
253
254         if( !ReadConsoleA( con.in, cbuf, 1, &nread, NULL ) )
255             log_fatal("ReadConsole failed: rc=%d", (int)GetLastError() );
256         if( !nread )
257             continue;
258         if( *cbuf == '\n' )
259             break;
260
261         if( !hidden )
262             last_prompt_len++;
263         c = *cbuf;
264         if( c == '\t' )
265             c = ' ';
266         else if( c > 0xa0 )
267             ; /* we don't allow 0xa0, as this is a protected blank which may
268                * confuse the user */
269         else if( iscntrl(c) )
270             continue;
271         if( !(i < n-1) ) {
272             n += 50;
273             buf = m_realloc( buf, n );
274         }
275         buf[i++] = c;
276     }
277
278     if( hidden )
279         SetConsoleMode(con.in, DEF_INPMODE );
280
281   #else /* unix version */
282     if( hidden ) {
283       #ifdef HAVE_TCGETATTR
284         struct termios term;
285
286         if( tcgetattr(fileno(ttyfp), &termsave) )
287             log_fatal("tcgetattr() failed: %s\n", strerror(errno) );
288         restore_termios = 1;
289         term = termsave;
290         term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
291         if( tcsetattr( fileno(ttyfp), TCSAFLUSH, &term ) )
292             log_fatal("tcsetattr() failed: %s\n", strerror(errno) );
293       #endif
294     }
295
296     /* fixme: How can we avoid that the \n is echoed w/o disabling
297      * canonical mode - w/o this kill_prompt can't work */
298     while( read(fileno(ttyfp), cbuf, 1) == 1 && *cbuf != '\n' ) {
299         if( !hidden )
300             last_prompt_len++;
301         c = *cbuf;
302         if( c == CONTROL_D )
303             log_info("control d found\n");
304         if( c == '\t' )
305             c = ' ';
306         else if( c > 0xa0 )
307             ; /* we don't allow 0xa0, as this is a protected blank which may
308                * confuse the user */
309         else if( iscntrl(c) )
310             continue;
311         if( !(i < n-1) ) {
312             n += 50;
313             buf = m_realloc( buf, n );
314         }
315         buf[i++] = c;
316     }
317     if( *cbuf != '\n' ) {
318         buf[0] = CONTROL_D;
319         i = 1;
320     }
321
322
323     if( hidden ) {
324       #ifdef HAVE_TCGETATTR
325         if( tcsetattr(fileno(ttyfp), TCSAFLUSH, &termsave) )
326             log_error("tcsetattr() failed: %s\n", strerror(errno) );
327         restore_termios = 0;
328       #endif
329     }
330   #endif /* end unix version */
331     buf[i] = 0;
332     return buf;
333 }
334
335
336 char *
337 tty_get( const char *prompt )
338 {
339     return do_get( prompt, 0 );
340 }
341
342 char *
343 tty_get_hidden( const char *prompt )
344 {
345     return do_get( prompt, 1 );
346 }
347
348
349 void
350 tty_kill_prompt()
351 {
352
353     if( !initialized )
354         init_ttyfp();
355     if( batchmode )
356         last_prompt_len = 0;
357     if( !last_prompt_len )
358         return;
359   #if __MINGW32__
360     tty_printf("\r%*s\r", last_prompt_len, "");
361   #else
362     {
363         int i;
364         putc('\r', ttyfp);
365         for(i=0; i < last_prompt_len; i ++ )
366             putc(' ', ttyfp);
367         putc('\r', ttyfp);
368         fflush(ttyfp);
369     }
370   #endif
371     last_prompt_len = 0;
372 }
373
374
375 int
376 tty_get_answer_is_yes( const char *prompt )
377 {
378     int yes;
379     char *p = tty_get( prompt );
380     tty_kill_prompt();
381     yes = answer_is_yes(p);
382     m_free(p);
383     return yes;
384 }
385