mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-23 10:29:58 +01:00
ed0d33f1d0
to libgcrypt functions, using shared error codes from libgpg-error, replacing the old functions we used to have in ../util by those in ../jnlib and ../common, renaming the malloc functions and a couple of types. Note, that not all changes are listed below becuause they are too similar and done at far too many places. As of today the code builds using the current libgcrypt from CVS but it is very unlikely that it actually works.
509 lines
11 KiB
C
509 lines
11 KiB
C
/* ttyio.c - tty i/O functions
|
|
* Copyright (C) 1998,1999,2000,2001,2002,2003 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 <config.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <unistd.h>
|
|
#ifdef HAVE_TCGETATTR
|
|
#include <termios.h>
|
|
#else
|
|
#ifdef HAVE_TERMIO_H
|
|
/* simulate termios with termio */
|
|
#include <termio.h>
|
|
#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 <windows.h>
|
|
#ifdef HAVE_TCGETATTR
|
|
#error mingw32 and termios
|
|
#endif
|
|
#endif
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
#include "util.h"
|
|
#include "memory.h"
|
|
#include "ttyio.h"
|
|
|
|
#define CONTROL_D ('D' - 'A' + 1)
|
|
|
|
#ifdef __MINGW32__ /* use the odd Win32 functions */
|
|
static struct {
|
|
HANDLE in, out;
|
|
} con;
|
|
#define DEF_INPMODE (ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT \
|
|
|ENABLE_PROCESSED_INPUT )
|
|
#define HID_INPMODE (ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT )
|
|
#define DEF_OUTMODE (ENABLE_WRAP_AT_EOL_OUTPUT|ENABLE_PROCESSED_OUTPUT)
|
|
|
|
#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
|
|
|
|
|
|
|
|
/* This is a wrapper around ttyname so that we can use it even when
|
|
the standard streams are redirected. It figures the name out the
|
|
first time and returns it in a statically allocated buffer. */
|
|
const char *
|
|
tty_get_ttyname (void)
|
|
{
|
|
static char *name;
|
|
|
|
/* On a GNU system ctermid() always return /dev/tty, so this does
|
|
not make much sense - however if it is ever changed we do the
|
|
Right Thing now. */
|
|
#ifdef HAVE_CTERMID
|
|
static int got_name;
|
|
|
|
if (!got_name)
|
|
{
|
|
const char *s;
|
|
s = ctermid (NULL);
|
|
if (s)
|
|
name = strdup (s);
|
|
got_name = 1;
|
|
}
|
|
#endif
|
|
/* Assume the staandrd tty on memory error or when tehre is no
|
|
certmid. */
|
|
return name? name : "/dev/tty";
|
|
}
|
|
|
|
|
|
|
|
#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(ttyfp), TCSAFLUSH, &termsave) )
|
|
log_error("tcsetattr() failed: %s\n", strerror(errno) );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
init_ttyfp(void)
|
|
{
|
|
if( initialized )
|
|
return;
|
|
|
|
#if defined(__MINGW32__)
|
|
{
|
|
SECURITY_ATTRIBUTES sa;
|
|
|
|
memset(&sa, 0, sizeof(sa));
|
|
sa.nLength = sizeof(sa);
|
|
sa.bInheritHandle = TRUE;
|
|
con.out = CreateFileA( "CONOUT$", GENERIC_READ|GENERIC_WRITE,
|
|
FILE_SHARE_READ|FILE_SHARE_WRITE,
|
|
&sa, OPEN_EXISTING, 0, 0 );
|
|
if( con.out == INVALID_HANDLE_VALUE )
|
|
log_fatal("open(CONOUT$) failed: rc=%d", (int)GetLastError() );
|
|
memset(&sa, 0, sizeof(sa));
|
|
sa.nLength = sizeof(sa);
|
|
sa.bInheritHandle = TRUE;
|
|
con.in = CreateFileA( "CONIN$", GENERIC_READ|GENERIC_WRITE,
|
|
FILE_SHARE_READ|FILE_SHARE_WRITE,
|
|
&sa, OPEN_EXISTING, 0, 0 );
|
|
if( con.in == INVALID_HANDLE_VALUE )
|
|
log_fatal("open(CONIN$) failed: rc=%d", (int)GetLastError() );
|
|
}
|
|
SetConsoleMode(con.in, DEF_INPMODE );
|
|
SetConsoleMode(con.out, DEF_OUTMODE );
|
|
|
|
#elif defined(__EMX__)
|
|
ttyfp = stdout; /* Fixme: replace by the real functions: see wklib */
|
|
#else
|
|
ttyfp = batchmode? stderr : fopen (tty_get_ttyname (), "r+");
|
|
if( !ttyfp ) {
|
|
log_error("cannot open `%s': %s\n", tty_get_ttyname (),
|
|
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__
|
|
{
|
|
char *buf = NULL;
|
|
int n;
|
|
DWORD nwritten;
|
|
|
|
n = vasprintf(&buf, fmt, arg_ptr);
|
|
if( !buf )
|
|
log_bug("vasprintf() failed\n");
|
|
|
|
if( !WriteConsoleA( con.out, buf, n, &nwritten, NULL ) )
|
|
log_fatal("WriteConsole failed: rc=%d", (int)GetLastError() );
|
|
if( n != nwritten )
|
|
log_fatal("WriteConsole failed: %d != %d\n", n, (int)nwritten );
|
|
last_prompt_len += n;
|
|
xfree (buf);
|
|
}
|
|
#else
|
|
last_prompt_len += vfprintf(ttyfp,fmt,arg_ptr) ;
|
|
fflush(ttyfp);
|
|
#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('\\', ttyfp);
|
|
if( *p == '\n' )
|
|
putc('n', ttyfp);
|
|
else if( !*p )
|
|
putc('0', ttyfp);
|
|
else
|
|
fprintf(ttyfp, "x%02x", *p );
|
|
}
|
|
else
|
|
putc(*p, ttyfp);
|
|
#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( max_n && (strlen( buf ) > max_n )) {
|
|
buf[max_n] = 0;
|
|
}
|
|
/*(utf8 conversion already does the control character quoting)*/
|
|
tty_printf("%s", buf );
|
|
xfree( buf );
|
|
}
|
|
else {
|
|
if( max_n && (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, 0 );
|
|
}
|
|
|
|
|
|
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 = xmalloc((n=50));
|
|
i = 0;
|
|
|
|
#ifdef __MINGW32__ /* windoze version */
|
|
if( hidden )
|
|
SetConsoleMode(con.in, HID_INPMODE );
|
|
|
|
for(;;) {
|
|
DWORD nread;
|
|
|
|
if( !ReadConsoleA( con.in, cbuf, 1, &nread, NULL ) )
|
|
log_fatal("ReadConsole failed: rc=%d", (int)GetLastError() );
|
|
if( !nread )
|
|
continue;
|
|
if( *cbuf == '\n' )
|
|
break;
|
|
|
|
if( !hidden )
|
|
last_prompt_len++;
|
|
c = *cbuf;
|
|
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 = xrealloc (buf, n);
|
|
}
|
|
buf[i++] = c;
|
|
}
|
|
|
|
if( hidden )
|
|
SetConsoleMode(con.in, DEF_INPMODE );
|
|
|
|
#elif defined(__riscos__)
|
|
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 = xrealloc (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 */
|
|
if( hidden ) {
|
|
#ifdef HAVE_TCGETATTR
|
|
struct termios term;
|
|
|
|
if( tcgetattr(fileno(ttyfp), &termsave) )
|
|
log_fatal("tcgetattr() failed: %s\n", strerror(errno) );
|
|
restore_termios = 1;
|
|
term = termsave;
|
|
term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
|
|
if( tcsetattr( fileno(ttyfp), 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(ttyfp), 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 = xrealloc (buf, n );
|
|
}
|
|
buf[i++] = c;
|
|
}
|
|
if( *cbuf != '\n' ) {
|
|
buf[0] = CONTROL_D;
|
|
i = 1;
|
|
}
|
|
|
|
|
|
if( hidden ) {
|
|
#ifdef HAVE_TCGETATTR
|
|
if( tcsetattr(fileno(ttyfp), 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', ttyfp);
|
|
for(i=0; i < last_prompt_len; i ++ )
|
|
putc(' ', ttyfp);
|
|
putc('\r', ttyfp);
|
|
fflush(ttyfp);
|
|
}
|
|
#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);
|
|
xfree(p);
|
|
return yes;
|
|
}
|