* cardglue.c (card_close): New.
[gnupg.git] / common / ttyio.c
1 /* ttyio.c -  tty i/O functions
2  * Copyright (C) 1998,1999,2000,2001,2002,2003 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 #else
30 #ifdef HAVE_TERMIO_H
31 /* simulate termios with termio */
32 #include <termio.h>
33 #define termios termio
34 #define tcsetattr ioctl
35 #define TCSAFLUSH TCSETAF
36 #define tcgetattr(A,B) ioctl(A,TCGETA,B)
37 #define HAVE_TCGETATTR
38 #endif
39 #endif
40 #ifdef __MINGW32__ /* use the odd Win32 functions */
41 #include <windows.h>
42 #ifdef HAVE_TCGETATTR
43 #error mingw32 and termios
44 #endif
45 #endif
46 #include <errno.h>
47 #include <ctype.h>
48 #include "util.h"
49 #include "memory.h"
50 #include "ttyio.h"
51
52 #define CONTROL_D ('D' - 'A' + 1)
53
54 #ifdef __MINGW32__ /* use the odd Win32 functions */
55 static struct {
56     HANDLE in, out;
57 } con;
58 #define DEF_INPMODE  (ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT    \
59                                         |ENABLE_PROCESSED_INPUT )
60 #define HID_INPMODE  (ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT )
61 #define DEF_OUTMODE  (ENABLE_WRAP_AT_EOL_OUTPUT|ENABLE_PROCESSED_OUTPUT)
62
63 #else /* yeah, we have a real OS */
64 static FILE *ttyfp = NULL;
65 #endif
66
67 static int initialized;
68 static int last_prompt_len;
69 static int batchmode;
70 static int no_terminal;
71
72 #ifdef HAVE_TCGETATTR
73     static struct termios termsave;
74     static int restore_termios;
75 #endif
76
77
78
79 /* This is a wrapper around ttyname so that we can use it even when
80    the standard streams are redirected.  It figures the name out the
81    first time and returns it in a statically allocated buffer. */
82 const char *
83 tty_get_ttyname (void)
84 {
85   static char *name;
86
87   /* On a GNU system ctermid() always return /dev/tty, so this does
88      not make much sense - however if it is ever changed we do the
89      Right Thing now. */
90 #ifdef HAVE_CTERMID
91   static int got_name;
92
93   if (!got_name)
94     {
95       const char *s;
96       s = ctermid (NULL);
97       if (s)
98         name = strdup (s);
99       got_name = 1;
100     }
101 #endif
102   /* Assume the staandrd tty on memory error or when tehre is no
103      certmid. */
104   return name? name : "/dev/tty";
105 }
106
107
108
109 #ifdef HAVE_TCGETATTR
110 static void
111 cleanup(void)
112 {
113     if( restore_termios ) {
114         restore_termios = 0; /* do it prios in case it is interrupted again */
115         if( tcsetattr(fileno(ttyfp), TCSAFLUSH, &termsave) )
116             log_error("tcsetattr() failed: %s\n", strerror(errno) );
117     }
118 }
119 #endif
120
121 static void
122 init_ttyfp(void)
123 {
124     if( initialized )
125         return;
126
127 #if defined(__MINGW32__)
128     {
129         SECURITY_ATTRIBUTES sa;
130
131         memset(&sa, 0, sizeof(sa));
132         sa.nLength = sizeof(sa);
133         sa.bInheritHandle = TRUE;
134         con.out = CreateFileA( "CONOUT$", GENERIC_READ|GENERIC_WRITE,
135                                FILE_SHARE_READ|FILE_SHARE_WRITE,
136                                &sa, OPEN_EXISTING, 0, 0 );
137         if( con.out == INVALID_HANDLE_VALUE )
138             log_fatal("open(CONOUT$) failed: rc=%d", (int)GetLastError() );
139         memset(&sa, 0, sizeof(sa));
140         sa.nLength = sizeof(sa);
141         sa.bInheritHandle = TRUE;
142         con.in = CreateFileA( "CONIN$", GENERIC_READ|GENERIC_WRITE,
143                                FILE_SHARE_READ|FILE_SHARE_WRITE,
144                                &sa, OPEN_EXISTING, 0, 0 );
145         if( con.in == INVALID_HANDLE_VALUE )
146             log_fatal("open(CONIN$) failed: rc=%d", (int)GetLastError() );
147     }
148     SetConsoleMode(con.in, DEF_INPMODE );
149     SetConsoleMode(con.out, DEF_OUTMODE );
150
151 #elif defined(__EMX__)
152     ttyfp = stdout; /* Fixme: replace by the real functions: see wklib */
153 #else
154     ttyfp = batchmode? stderr : fopen (tty_get_ttyname (), "r+");
155     if( !ttyfp ) {
156         log_error("cannot open `%s': %s\n", tty_get_ttyname (),
157                   strerror(errno) );
158         exit(2);
159     }
160 #endif
161 #ifdef HAVE_TCGETATTR
162     atexit( cleanup );
163 #endif
164     initialized = 1;
165 }
166
167
168 int
169 tty_batchmode( int onoff )
170 {
171     int old = batchmode;
172     if( onoff != -1 )
173         batchmode = onoff;
174     return old;
175 }
176
177 int
178 tty_no_terminal(int onoff)
179 {
180     int old = no_terminal;
181     no_terminal = onoff ? 1 : 0;
182     return old;
183 }
184
185 void
186 tty_printf( const char *fmt, ... )
187 {
188     va_list arg_ptr;
189
190     if (no_terminal)
191         return;
192
193     if( !initialized )
194         init_ttyfp();
195
196     va_start( arg_ptr, fmt ) ;
197 #ifdef __MINGW32__
198     {   
199         char *buf = NULL;
200         int n;
201         DWORD nwritten;
202
203         n = vasprintf(&buf, fmt, arg_ptr);
204         if( !buf )
205             log_bug("vasprintf() failed\n");
206         
207         if( !WriteConsoleA( con.out, buf, n, &nwritten, NULL ) )
208             log_fatal("WriteConsole failed: rc=%d", (int)GetLastError() );
209         if( n != nwritten )
210             log_fatal("WriteConsole failed: %d != %d\n", n, (int)nwritten );
211         last_prompt_len += n;
212         xfree (buf);
213     }
214 #else
215     last_prompt_len += vfprintf(ttyfp,fmt,arg_ptr) ;
216     fflush(ttyfp);
217 #endif
218     va_end(arg_ptr);
219 }
220
221
222 /****************
223  * Print a string, but filter all control characters out.
224  */
225 void
226 tty_print_string( byte *p, size_t n )
227 {
228     if (no_terminal)
229         return;
230
231     if( !initialized )
232         init_ttyfp();
233
234 #ifdef __MINGW32__
235     /* not so effective, change it if you want */
236     for( ; n; n--, p++ )
237         if( iscntrl( *p ) ) {
238             if( *p == '\n' )
239                 tty_printf("\\n");
240             else if( !*p )
241                 tty_printf("\\0");
242             else
243                 tty_printf("\\x%02x", *p);
244         }
245         else
246             tty_printf("%c", *p);
247 #else
248     for( ; n; n--, p++ )
249         if( iscntrl( *p ) ) {
250             putc('\\', ttyfp);
251             if( *p == '\n' )
252                 putc('n', ttyfp);
253             else if( !*p )
254                 putc('0', ttyfp);
255             else
256                 fprintf(ttyfp, "x%02x", *p );
257         }
258         else
259             putc(*p, ttyfp);
260 #endif
261 }
262
263 void
264 tty_print_utf8_string2( byte *p, size_t n, size_t max_n )
265 {
266     size_t i;
267     char *buf;
268
269     if (no_terminal)
270         return;
271
272     /* we can handle plain ascii simpler, so check for it first */
273     for(i=0; i < n; i++ ) {
274         if( p[i] & 0x80 )
275             break;
276     }
277     if( i < n ) {
278         buf = utf8_to_native( p, n, 0 );
279         if( max_n && (strlen( buf ) > max_n )) {
280             buf[max_n] = 0;
281         }
282         /*(utf8 conversion already does the control character quoting)*/
283         tty_printf("%s", buf );
284         xfree( buf );
285     }
286     else {
287         if( max_n && (n > max_n) ) {
288             n = max_n;
289         }
290         tty_print_string( p, n );
291     }
292 }
293
294 void
295 tty_print_utf8_string( byte *p, size_t n )
296 {
297     tty_print_utf8_string2( p, n, 0 );
298 }
299
300
301 static char *
302 do_get( const char *prompt, int hidden )
303 {
304     char *buf;
305 #ifndef __riscos__
306     byte cbuf[1];
307 #endif
308     int c, n, i;
309
310     if( batchmode ) {
311         log_error("Sorry, we are in batchmode - can't get input\n");
312         exit(2);
313     }
314
315     if (no_terminal) {
316         log_error("Sorry, no terminal at all requested - can't get input\n");
317         exit(2);
318     }
319
320     if( !initialized )
321         init_ttyfp();
322
323     last_prompt_len = 0;
324     tty_printf( "%s", prompt );
325     buf = xmalloc((n=50));
326     i = 0;
327
328 #ifdef __MINGW32__ /* windoze version */
329     if( hidden )
330         SetConsoleMode(con.in, HID_INPMODE );
331
332     for(;;) {
333         DWORD nread;
334
335         if( !ReadConsoleA( con.in, cbuf, 1, &nread, NULL ) )
336             log_fatal("ReadConsole failed: rc=%d", (int)GetLastError() );
337         if( !nread )
338             continue;
339         if( *cbuf == '\n' )
340             break;
341
342         if( !hidden )
343             last_prompt_len++;
344         c = *cbuf;
345         if( c == '\t' )
346             c = ' ';
347         else if( c > 0xa0 )
348             ; /* we don't allow 0xa0, as this is a protected blank which may
349                * confuse the user */
350         else if( iscntrl(c) )
351             continue;
352         if( !(i < n-1) ) {
353             n += 50;
354             buf = xrealloc (buf, n);
355         }
356         buf[i++] = c;
357     }
358
359     if( hidden )
360         SetConsoleMode(con.in, DEF_INPMODE );
361
362 #elif defined(__riscos__)
363     do {
364         c = riscos_getchar();
365         if (c == 0xa || c == 0xd) { /* Return || Enter */
366             c = (int) '\n';
367         } else if (c == 0x8 || c == 0x7f) { /* Backspace || Delete */
368             if (i>0) {
369                 i--;
370                 if (!hidden) {
371                     last_prompt_len--;
372                     fputc(8, ttyfp);
373                     fputc(32, ttyfp);
374                     fputc(8, ttyfp);
375                     fflush(ttyfp);
376                 }
377             } else {
378                 fputc(7, ttyfp);
379                 fflush(ttyfp);
380             }
381             continue;
382         } else if (c == (int) '\t') { /* Tab */
383             c = ' ';
384         } else if (c > 0xa0) {
385             ; /* we don't allow 0xa0, as this is a protected blank which may
386                * confuse the user */
387         } else if (iscntrl(c)) {
388             continue;
389         }
390         if(!(i < n-1)) {
391             n += 50;
392             buf = xrealloc (buf, n);
393         }
394         buf[i++] = c;
395         if (!hidden) {
396             last_prompt_len++;
397             fputc(c, ttyfp);
398             fflush(ttyfp);
399         }
400     } while (c != '\n');
401     i = (i>0) ? i-1 : 0;
402 #else /* unix version */
403     if( hidden ) {
404 #ifdef HAVE_TCGETATTR
405         struct termios term;
406
407         if( tcgetattr(fileno(ttyfp), &termsave) )
408             log_fatal("tcgetattr() failed: %s\n", strerror(errno) );
409         restore_termios = 1;
410         term = termsave;
411         term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
412         if( tcsetattr( fileno(ttyfp), TCSAFLUSH, &term ) )
413             log_fatal("tcsetattr() failed: %s\n", strerror(errno) );
414 #endif
415     }
416
417     /* fixme: How can we avoid that the \n is echoed w/o disabling
418      * canonical mode - w/o this kill_prompt can't work */
419     while( read(fileno(ttyfp), cbuf, 1) == 1 && *cbuf != '\n' ) {
420         if( !hidden )
421             last_prompt_len++;
422         c = *cbuf;
423         if( c == CONTROL_D )
424             log_info("control d found\n");
425         if( c == '\t' )
426             c = ' ';
427         else if( c > 0xa0 )
428             ; /* we don't allow 0xa0, as this is a protected blank which may
429                * confuse the user */
430         else if( iscntrl(c) )
431             continue;
432         if( !(i < n-1) ) {
433             n += 50;
434             buf = xrealloc (buf, n );
435         }
436         buf[i++] = c;
437     }
438     if( *cbuf != '\n' ) {
439         buf[0] = CONTROL_D;
440         i = 1;
441     }
442
443
444     if( hidden ) {
445 #ifdef HAVE_TCGETATTR
446         if( tcsetattr(fileno(ttyfp), TCSAFLUSH, &termsave) )
447             log_error("tcsetattr() failed: %s\n", strerror(errno) );
448         restore_termios = 0;
449 #endif
450     }
451 #endif /* end unix version */
452     buf[i] = 0;
453     return buf;
454 }
455
456
457 char *
458 tty_get( const char *prompt )
459 {
460     return do_get( prompt, 0 );
461 }
462
463 char *
464 tty_get_hidden( const char *prompt )
465 {
466     return do_get( prompt, 1 );
467 }
468
469
470 void
471 tty_kill_prompt()
472 {
473     if ( no_terminal )
474         return;
475
476     if( !initialized )
477         init_ttyfp();
478
479     if( batchmode )
480         last_prompt_len = 0;
481     if( !last_prompt_len )
482         return;
483 #ifdef __MINGW32__
484     tty_printf("\r%*s\r", last_prompt_len, "");
485 #else
486     {
487         int i;
488         putc('\r', ttyfp);
489         for(i=0; i < last_prompt_len; i ++ )
490             putc(' ', ttyfp);
491         putc('\r', ttyfp);
492         fflush(ttyfp);
493     }
494 #endif
495     last_prompt_len = 0;
496 }
497
498
499 int
500 tty_get_answer_is_yes( const char *prompt )
501 {
502     int yes;
503     char *p = tty_get( prompt );
504     tty_kill_prompt();
505     yes = answer_is_yes(p);
506     xfree(p);
507     return yes;
508 }