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