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