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