5749c59fed8b5dbc85342c599aa196df153e4dce
[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 _WIN32 /* 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 _WIN32 /* 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(_WIN32)
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 _WIN32
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 /* Same as tty_printf but if FP is not NULL, behave like a regualr
223    fprintf. */
224 void
225 tty_fprintf (FILE *fp, const char *fmt, ... )
226 {
227   va_list arg_ptr;
228
229   if (fp)
230     {
231       va_start (arg_ptr, fmt) ;
232       vfprintf (fp, fmt, arg_ptr );
233       va_end (arg_ptr);
234       return;
235     }
236
237   if (no_terminal)
238     return;
239
240   if( !initialized )
241     init_ttyfp();
242
243     va_start( arg_ptr, fmt ) ;
244 #ifdef _WIN32
245     {   
246         char *buf = NULL;
247         int n;
248         DWORD nwritten;
249
250         n = vasprintf(&buf, fmt, arg_ptr);
251         if( !buf )
252             log_bug("vasprintf() failed\n");
253         
254         if( !WriteConsoleA( con.out, buf, n, &nwritten, NULL ) )
255             log_fatal("WriteConsole failed: rc=%d", (int)GetLastError() );
256         if( n != nwritten )
257             log_fatal("WriteConsole failed: %d != %d\n", n, (int)nwritten );
258         last_prompt_len += n;
259         xfree (buf);
260     }
261 #else
262     last_prompt_len += vfprintf(ttyfp,fmt,arg_ptr) ;
263     fflush(ttyfp);
264 #endif
265     va_end(arg_ptr);
266 }
267
268
269 /****************
270  * Print a string, but filter all control characters out.
271  */
272 void
273 tty_print_string ( const byte *p, size_t n )
274 {
275     if (no_terminal)
276         return;
277
278     if( !initialized )
279         init_ttyfp();
280
281 #ifdef _WIN32
282     /* not so effective, change it if you want */
283     for( ; n; n--, p++ )
284         if( iscntrl( *p ) ) {
285             if( *p == '\n' )
286                 tty_printf("\\n");
287             else if( !*p )
288                 tty_printf("\\0");
289             else
290                 tty_printf("\\x%02x", *p);
291         }
292         else
293             tty_printf("%c", *p);
294 #else
295     for( ; n; n--, p++ )
296         if( iscntrl( *p ) ) {
297             putc('\\', ttyfp);
298             if( *p == '\n' )
299                 putc('n', ttyfp);
300             else if( !*p )
301                 putc('0', ttyfp);
302             else
303                 fprintf(ttyfp, "x%02x", *p );
304         }
305         else
306             putc(*p, ttyfp);
307 #endif
308 }
309
310 void
311 tty_print_utf8_string2( const byte *p, size_t n, size_t max_n )
312 {
313     size_t i;
314     char *buf;
315
316     if (no_terminal)
317         return;
318
319     /* we can handle plain ascii simpler, so check for it first */
320     for(i=0; i < n; i++ ) {
321         if( p[i] & 0x80 )
322             break;
323     }
324     if( i < n ) {
325         buf = utf8_to_native( (const char *)p, n, 0 );
326         if( max_n && (strlen( buf ) > max_n )) {
327             buf[max_n] = 0;
328         }
329         /*(utf8 conversion already does the control character quoting)*/
330         tty_printf("%s", buf );
331         xfree( buf );
332     }
333     else {
334         if( max_n && (n > max_n) ) {
335             n = max_n;
336         }
337         tty_print_string( p, n );
338     }
339 }
340
341 void
342 tty_print_utf8_string( const byte *p, size_t n )
343 {
344     tty_print_utf8_string2( p, n, 0 );
345 }
346
347
348 static char *
349 do_get( const char *prompt, int hidden )
350 {
351     char *buf;
352 #ifndef __riscos__
353     byte cbuf[1];
354 #endif
355     int c, n, i;
356
357     if( batchmode ) {
358         log_error("Sorry, we are in batchmode - can't get input\n");
359         exit(2);
360     }
361
362     if (no_terminal) {
363         log_error("Sorry, no terminal at all requested - can't get input\n");
364         exit(2);
365     }
366
367     if( !initialized )
368         init_ttyfp();
369
370     last_prompt_len = 0;
371     tty_printf( "%s", prompt );
372     buf = xmalloc((n=50));
373     i = 0;
374
375 #ifdef _WIN32 /* windoze version */
376     if( hidden )
377         SetConsoleMode(con.in, HID_INPMODE );
378
379     for(;;) {
380         DWORD nread;
381
382         if( !ReadConsoleA( con.in, cbuf, 1, &nread, NULL ) )
383             log_fatal("ReadConsole failed: rc=%d", (int)GetLastError() );
384         if( !nread )
385             continue;
386         if( *cbuf == '\n' )
387             break;
388
389         if( !hidden )
390             last_prompt_len++;
391         c = *cbuf;
392         if( c == '\t' )
393             c = ' ';
394         else if( c > 0xa0 )
395             ; /* we don't allow 0xa0, as this is a protected blank which may
396                * confuse the user */
397         else if( iscntrl(c) )
398             continue;
399         if( !(i < n-1) ) {
400             n += 50;
401             buf = xrealloc (buf, n);
402         }
403         buf[i++] = c;
404     }
405
406     if( hidden )
407         SetConsoleMode(con.in, DEF_INPMODE );
408
409 #elif defined(__riscos__)
410     do {
411         c = riscos_getchar();
412         if (c == 0xa || c == 0xd) { /* Return || Enter */
413             c = (int) '\n';
414         } else if (c == 0x8 || c == 0x7f) { /* Backspace || Delete */
415             if (i>0) {
416                 i--;
417                 if (!hidden) {
418                     last_prompt_len--;
419                     fputc(8, ttyfp);
420                     fputc(32, ttyfp);
421                     fputc(8, ttyfp);
422                     fflush(ttyfp);
423                 }
424             } else {
425                 fputc(7, ttyfp);
426                 fflush(ttyfp);
427             }
428             continue;
429         } else if (c == (int) '\t') { /* Tab */
430             c = ' ';
431         } else if (c > 0xa0) {
432             ; /* we don't allow 0xa0, as this is a protected blank which may
433                * confuse the user */
434         } else if (iscntrl(c)) {
435             continue;
436         }
437         if(!(i < n-1)) {
438             n += 50;
439             buf = xrealloc (buf, n);
440         }
441         buf[i++] = c;
442         if (!hidden) {
443             last_prompt_len++;
444             fputc(c, ttyfp);
445             fflush(ttyfp);
446         }
447     } while (c != '\n');
448     i = (i>0) ? i-1 : 0;
449 #else /* unix version */
450     if( hidden ) {
451 #ifdef HAVE_TCGETATTR
452         struct termios term;
453
454         if( tcgetattr(fileno(ttyfp), &termsave) )
455             log_fatal("tcgetattr() failed: %s\n", strerror(errno) );
456         restore_termios = 1;
457         term = termsave;
458         term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
459         if( tcsetattr( fileno(ttyfp), TCSAFLUSH, &term ) )
460             log_fatal("tcsetattr() failed: %s\n", strerror(errno) );
461 #endif
462     }
463
464     /* fixme: How can we avoid that the \n is echoed w/o disabling
465      * canonical mode - w/o this kill_prompt can't work */
466     while( read(fileno(ttyfp), cbuf, 1) == 1 && *cbuf != '\n' ) {
467         if( !hidden )
468             last_prompt_len++;
469         c = *cbuf;
470         if( c == CONTROL_D )
471             log_info("control d found\n");
472         if( c == '\t' )
473             c = ' ';
474         else if( c > 0xa0 )
475             ; /* we don't allow 0xa0, as this is a protected blank which may
476                * confuse the user */
477         else if( iscntrl(c) )
478             continue;
479         if( !(i < n-1) ) {
480             n += 50;
481             buf = xrealloc (buf, n );
482         }
483         buf[i++] = c;
484     }
485     if( *cbuf != '\n' ) {
486         buf[0] = CONTROL_D;
487         i = 1;
488     }
489
490
491     if( hidden ) {
492 #ifdef HAVE_TCGETATTR
493         if( tcsetattr(fileno(ttyfp), TCSAFLUSH, &termsave) )
494             log_error("tcsetattr() failed: %s\n", strerror(errno) );
495         restore_termios = 0;
496 #endif
497     }
498 #endif /* end unix version */
499     buf[i] = 0;
500     return buf;
501 }
502
503
504 char *
505 tty_get( const char *prompt )
506 {
507     return do_get( prompt, 0 );
508 }
509
510 char *
511 tty_get_hidden( const char *prompt )
512 {
513     return do_get( prompt, 1 );
514 }
515
516
517 void
518 tty_kill_prompt()
519 {
520     if ( no_terminal )
521         return;
522
523     if( !initialized )
524         init_ttyfp();
525
526     if( batchmode )
527         last_prompt_len = 0;
528     if( !last_prompt_len )
529         return;
530 #ifdef _WIN32
531     tty_printf("\r%*s\r", last_prompt_len, "");
532 #else
533     {
534         int i;
535         putc('\r', ttyfp);
536         for(i=0; i < last_prompt_len; i ++ )
537             putc(' ', ttyfp);
538         putc('\r', ttyfp);
539         fflush(ttyfp);
540     }
541 #endif
542     last_prompt_len = 0;
543 }
544
545
546 int
547 tty_get_answer_is_yes( const char *prompt )
548 {
549     int yes;
550     char *p = tty_get( prompt );
551     tty_kill_prompt();
552     yes = answer_is_yes(p);
553     xfree(p);
554     return yes;
555 }