/* ttyio.c - tty i/O functions * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include #include #include #include #include #include #ifdef HAVE_TCGETATTR #include #else #ifdef HAVE_TERMIO_H /* simulate termios with termio */ #include #define termios termio #define tcsetattr ioctl #define TCSAFLUSH TCSETAF #define tcgetattr(A,B) ioctl(A,TCGETA,B) #define HAVE_TCGETATTR #endif #endif #ifdef __MINGW32__ /* use the odd Win32 functions */ #include /* #ifdef HAVE_TCGETATTR #error mingw32 and termios #endif */ #endif #include #include #include "util.h" #include "memory.h" #include "ttyio.h" #define CONTROL_D ('D' - 'A' + 1) #ifdef __VMS #define TERMDEVICE "/dev/tty" #else #define TERMDEVICE "/dev/tty" #endif /*#ifdef __MINGW32__ use the odd Win32 functions * static FILE *ttyfp_in = NULL; static FILE *ttyfp_out = NULL; #else yeah, we have a real OS * static FILE *ttyfp = NULL; #endif */ static int initialized; static int last_prompt_len; static int batchmode; static int no_terminal; #ifdef HAVE_TCGETATTR static struct termios termsave; static int restore_termios; #endif #ifdef HAVE_TCGETATTR static void cleanup(void) { if( restore_termios ) { restore_termios = 0; /* do it prios in case it is interrupted again */ if( tcsetattr(fileno(stdin), TCSAFLUSH, &termsave) ) log_error("tcsetattr() failed: %s\n", strerror(errno) ); if( tcsetattr(fileno(stdout), TCSAFLUSH, &termsave) ) log_error("tcsetattr() failed: %s\n", strerror(errno) ); } } #endif static void init_ttyfp(void) { if( initialized ) return; /* #if defined(__MINGW32__) ttyfp_in = stdin; ttyfp_out = stdout; #elif defined(__EMX__) ttyfp = stdout; Fixme: replace by the real functions: see wklib * #else ttyfp = batchmode? stderr : stdout; if( !ttyfp ) { log_error("cannot open /dev/tty: %s\n", strerror(errno) ); exit(2); } #endif */ #ifdef HAVE_TCGETATTR atexit( cleanup ); #endif initialized = 1; } int tty_batchmode( int onoff ) { int old = batchmode; if( onoff != -1 ) batchmode = onoff; return old; } int tty_no_terminal(int onoff) { int old = no_terminal; no_terminal = onoff ? 1 : 0; return old; } void tty_printf( const char *fmt, ... ) { va_list arg_ptr; if (no_terminal) return; if( !initialized ) init_ttyfp(); va_start( arg_ptr, fmt ) ; /* #ifdef __MINGW32__ last_prompt_len += vfprintf(ttyfp_out,fmt,arg_ptr); fflush(ttyfp_out); #else */ last_prompt_len += vfprintf(stdout,fmt,arg_ptr) ; fflush(stdout); // #endif va_end(arg_ptr); } /**************** * Print a string, but filter all control characters out. */ void tty_print_string( byte *p, size_t n ) { if (no_terminal) return; if( !initialized ) init_ttyfp(); #ifdef __MINGW32__ /* not so effective, change it if you want */ for( ; n; n--, p++ ) if( iscntrl( *p ) ) { if( *p == '\n' ) tty_printf("\\n"); else if( !*p ) tty_printf("\\0"); else tty_printf("\\x%02x", *p); } else tty_printf("%c", *p); #else for( ; n; n--, p++ ) if( iscntrl( *p ) ) { putc('\\', stdout); if( *p == '\n' ) putc('n', stdout); else if( !*p ) putc('0', stdout); else fprintf(stdout, "x%02x", *p ); } else putc(*p, stdout); #endif } void tty_print_utf8_string2( byte *p, size_t n, size_t max_n ) { size_t i; char *buf; if (no_terminal) return; /* we can handle plain ascii simpler, so check for it first */ for(i=0; i < n; i++ ) { if( p[i] & 0x80 ) break; } if( i < n ) { buf = utf8_to_native( p, n, 0 ); if( strlen( buf ) > max_n ) { buf[max_n] = 0; } /*(utf8 conversion already does the control character quoting)*/ tty_printf("%s", buf ); m_free( buf ); } else { if( n > max_n ) { n = max_n; } tty_print_string( p, n ); } } void tty_print_utf8_string( byte *p, size_t n ) { tty_print_utf8_string2( p, n, n ); } static char * do_get( const char *prompt, int hidden ) { char *buf; #ifndef __riscos__ byte cbuf[1]; #endif int c, n, i; if( batchmode ) { log_error("Sorry, we are in batchmode - can't get input\n"); exit(2); } if (no_terminal) { log_error("Sorry, no terminal at all requested - can't get input\n"); exit(2); } if( !initialized ) init_ttyfp(); last_prompt_len = 0; tty_printf( "%s", prompt ); buf = m_alloc(n=50); i = 0; #ifdef __MINGW32__ /* windoze version */ #error NOT supported #elif defined(__riscos__) tty_printf("reading info from: (__riscos__)\n"); do { c = riscos_getchar(); if (c == 0xa || c == 0xd) { /* Return || Enter */ c = (int) '\n'; } else if (c == 0x8 || c == 0x7f) { /* Backspace || Delete */ if (i>0) { i--; if (!hidden) { last_prompt_len--; fputc(8, ttyfp); fputc(32, ttyfp); fputc(8, ttyfp); fflush(ttyfp); } } else { fputc(7, ttyfp); fflush(ttyfp); } continue; } else if (c == (int) '\t') { /* Tab */ c = ' '; } else if (c > 0xa0) { ; /* we don't allow 0xa0, as this is a protected blank which may * confuse the user */ } else if (iscntrl(c)) { continue; } if(!(i < n-1)) { n += 50; buf = m_realloc(buf, n); } buf[i++] = c; if (!hidden) { last_prompt_len++; fputc(c, ttyfp); fflush(ttyfp); } } while (c != '\n'); i = (i>0) ? i-1 : 0; #else /* unix version */ /* tty_printf("reading info from: (LINUX)\n"); */ /* if( hidden ) { #ifdef HAVE_TCGETATTR struct termios term; if( tcgetattr(fileno(stdin), &termsave) ) log_fatal("tcgetattr() failed -- here: %s\n", strerror(errno) ); restore_termios = 1; term = termsave; term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); if( tcsetattr( fileno(stdin), TCSAFLUSH, &term ) ) log_fatal("tcsetattr() failed: %s\n", strerror(errno) ); #endif } */ /* fixme: How can we avoid that the \n is echoed w/o disabling * canonical mode - w/o this kill_prompt can't work */ while( read(fileno(stdin), cbuf, 1) == 1 && *cbuf != '\n' ) { if( !hidden ) last_prompt_len++; c = *cbuf; if( c == CONTROL_D ) log_info("control d found\n"); if( c == '\t' ) c = ' '; else if( c > 0xa0 ) ; /* we don't allow 0xa0, as this is a protected blank which may * confuse the user */ else if( iscntrl(c) ) continue; if( !(i < n-1) ) { n += 50; buf = m_realloc( buf, n ); } buf[i++] = c; } if( *cbuf != '\n' ) { buf[0] = CONTROL_D; i = 1; } /* if( hidden ) { #ifdef HAVE_TCGETATTR if( tcsetattr(fileno(stdin), TCSAFLUSH, &termsave) ) log_error("tcsetattr() failed: %s\n", strerror(errno) ); restore_termios = 0; #endif } */ #endif /* end unix version */ buf[i] = 0; return buf; } char * tty_get( const char *prompt ) { return do_get( prompt, 0 ); } char * tty_get_hidden( const char *prompt ) { return do_get( prompt, 1 ); } void tty_kill_prompt() { if ( no_terminal ) return; if( !initialized ) init_ttyfp(); if( batchmode ) last_prompt_len = 0; if( !last_prompt_len ) return; #ifdef __MINGW32__ tty_printf("\r%*s\r", last_prompt_len, ""); #else { int i; putc('\r', stdout); for(i=0; i < last_prompt_len; i ++ ) putc(' ', stdout); putc('\r', stdout); fflush(stdout); } #endif last_prompt_len = 0; } int tty_get_answer_is_yes( const char *prompt ) { int yes; char *p = tty_get( prompt ); tty_kill_prompt(); yes = answer_is_yes(p); m_free(p); return yes; }