Convey locale-related environment variables as backup, and nicer error.
Fixes #74.
This commit is contained in:
@@ -76,6 +76,12 @@ UDP port number to bind
|
|||||||
.B \-c \fICOLORS\fP
|
.B \-c \fICOLORS\fP
|
||||||
Number of colors to advertise to applications through TERM (e.g. 8, 256)
|
Number of colors to advertise to applications through TERM (e.g. 8, 256)
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B \-l \fINAME=VALUE\fP
|
||||||
|
Locale-related environment variable to try as part of a fallback
|
||||||
|
environment, if the startup environment does not specify a character
|
||||||
|
set of UTF-8.
|
||||||
|
|
||||||
.SH EXAMPLE
|
.SH EXAMPLE
|
||||||
|
|
||||||
.nf
|
.nf
|
||||||
|
|||||||
+12
@@ -64,6 +64,18 @@ pass between client and server. By default, \fBmosh\fP uses the ports
|
|||||||
between 60000 and 61000, but allows the user to request a particular
|
between 60000 and 61000, but allows the user to request a particular
|
||||||
UDP port instead.
|
UDP port instead.
|
||||||
|
|
||||||
|
\fBmosh\fP will do its best to arrange a UTF-8 character set locale on
|
||||||
|
the client and server. The client must have locale-related environment
|
||||||
|
variables that specify UTF-8. \fBmosh\fP will pass these client
|
||||||
|
variables to the \fBmosh-server\fP on its command line, but in most
|
||||||
|
cases they will not need to be used. \fBmosh-server\fP first attempts
|
||||||
|
to use its own locale-related environment variables, which come from
|
||||||
|
the system default configuration (sometimes /etc/default/locale) or
|
||||||
|
from having been passed over the SSH connection. But if these
|
||||||
|
variables don't call for the use of UTF-8, \fBmosh-server\fP will
|
||||||
|
apply the locale-related environment variables from the client and try
|
||||||
|
again.
|
||||||
|
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.TP
|
.TP
|
||||||
.B \fIcommand\fP
|
.B \fIcommand\fP
|
||||||
|
|||||||
@@ -196,6 +196,10 @@ if ( $pid == 0 ) { # child
|
|||||||
push @server, ( '-p', $port_request );
|
push @server, ( '-p', $port_request );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for ( &locale_vars ) {
|
||||||
|
push @server, ( '-l', $_ );
|
||||||
|
}
|
||||||
|
|
||||||
if ( scalar @command > 0 ) {
|
if ( scalar @command > 0 ) {
|
||||||
push @server, '--', @command;
|
push @server, '--', @command;
|
||||||
}
|
}
|
||||||
@@ -237,3 +241,17 @@ if ( $pid == 0 ) { # child
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub shell_quote { join ' ', map {(my $a = $_) =~ s/'/'\\''/g; "'$a'"} @_ }
|
sub shell_quote { join ' ', map {(my $a = $_) =~ s/'/'\\''/g; "'$a'"} @_ }
|
||||||
|
|
||||||
|
sub locale_vars {
|
||||||
|
my @names = qw[LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION LC_ALL];
|
||||||
|
|
||||||
|
my @assignments;
|
||||||
|
|
||||||
|
for ( @names ) {
|
||||||
|
if ( defined $ENV{ $_ } ) {
|
||||||
|
push @assignments, $_ . q{=} . $ENV{ $_ };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return @assignments;
|
||||||
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ int main( void )
|
|||||||
|
|
||||||
/* Adopt native locale */
|
/* Adopt native locale */
|
||||||
set_native_locale();
|
set_native_locale();
|
||||||
assert_utf8_locale();
|
assert( is_utf8_locale() );
|
||||||
|
|
||||||
for ( int i = 0; i < ITERATIONS; i++ ) {
|
for ( int i = 0; i < ITERATIONS; i++ ) {
|
||||||
/* type a character */
|
/* type a character */
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ int main( int argc __attribute__((unused)),
|
|||||||
struct termios saved_termios, raw_termios, child_termios;
|
struct termios saved_termios, raw_termios, child_termios;
|
||||||
|
|
||||||
set_native_locale();
|
set_native_locale();
|
||||||
assert_utf8_locale();
|
assert( is_utf8_locale() );
|
||||||
|
|
||||||
if ( tcgetattr( STDIN_FILENO, &saved_termios ) < 0 ) {
|
if ( tcgetattr( STDIN_FILENO, &saved_termios ) < 0 ) {
|
||||||
perror( "tcgetattr" );
|
perror( "tcgetattr" );
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ int main( void )
|
|||||||
struct termios saved_termios, raw_termios, child_termios;
|
struct termios saved_termios, raw_termios, child_termios;
|
||||||
|
|
||||||
set_native_locale();
|
set_native_locale();
|
||||||
assert_utf8_locale();
|
assert( is_utf8_locale() );
|
||||||
|
|
||||||
if ( tcgetattr( STDIN_FILENO, &saved_termios ) < 0 ) {
|
if ( tcgetattr( STDIN_FILENO, &saved_termios ) < 0 ) {
|
||||||
perror( "tcgetattr" );
|
perror( "tcgetattr" );
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ using namespace std;
|
|||||||
|
|
||||||
void print_usage( const char *argv0 )
|
void print_usage( const char *argv0 )
|
||||||
{
|
{
|
||||||
fprintf( stderr, "Usage: %s new [-s] [-v] [-i LOCALADDR] [-p PORT] [-c COLORS] [-- COMMAND...]\n", argv0 );
|
fprintf( stderr, "Usage: %s new [-s] [-v] [-i LOCALADDR] [-p PORT] [-c COLORS] [-l NAME=VALUE] [-- COMMAND...]\n", argv0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Simple spinloop */
|
/* Simple spinloop */
|
||||||
@@ -113,6 +113,7 @@ int main( int argc, char *argv[] )
|
|||||||
int colors = 0;
|
int colors = 0;
|
||||||
bool verbose = false; /* don't close stdin/stdout/stderr */
|
bool verbose = false; /* don't close stdin/stdout/stderr */
|
||||||
/* Will cause mosh-server not to correctly detach on old versions of sshd. */
|
/* Will cause mosh-server not to correctly detach on old versions of sshd. */
|
||||||
|
list<string> locale_vars;
|
||||||
|
|
||||||
/* strip off command */
|
/* strip off command */
|
||||||
for ( int i = 0; i < argc; i++ ) {
|
for ( int i = 0; i < argc; i++ ) {
|
||||||
@@ -130,7 +131,7 @@ int main( int argc, char *argv[] )
|
|||||||
&& (strcmp( argv[ 1 ], "new" ) == 0) ) {
|
&& (strcmp( argv[ 1 ], "new" ) == 0) ) {
|
||||||
/* new option syntax */
|
/* new option syntax */
|
||||||
int opt;
|
int opt;
|
||||||
while ( (opt = getopt( argc - 1, argv + 1, "i:p:c:sv" )) != -1 ) {
|
while ( (opt = getopt( argc - 1, argv + 1, "i:p:c:svl:" )) != -1 ) {
|
||||||
switch ( opt ) {
|
switch ( opt ) {
|
||||||
case 'i':
|
case 'i':
|
||||||
desired_ip = optarg;
|
desired_ip = optarg;
|
||||||
@@ -148,6 +149,9 @@ int main( int argc, char *argv[] )
|
|||||||
case 'v':
|
case 'v':
|
||||||
verbose = true;
|
verbose = true;
|
||||||
break;
|
break;
|
||||||
|
case 'l':
|
||||||
|
locale_vars.push_back( string( optarg ) );
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
print_usage( argv[ 0 ] );
|
print_usage( argv[ 0 ] );
|
||||||
/* don't die on unknown options */
|
/* don't die on unknown options */
|
||||||
@@ -203,7 +207,28 @@ int main( int argc, char *argv[] )
|
|||||||
|
|
||||||
/* Adopt implementation locale */
|
/* Adopt implementation locale */
|
||||||
set_native_locale();
|
set_native_locale();
|
||||||
assert_utf8_locale();
|
if ( !is_utf8_locale() ) {
|
||||||
|
/* apply locale-related environment variables from client */
|
||||||
|
clear_locale_variables();
|
||||||
|
for ( list<string>::const_iterator i = locale_vars.begin();
|
||||||
|
i != locale_vars.end();
|
||||||
|
i++ ) {
|
||||||
|
char *env_string = strdup( i->c_str() );
|
||||||
|
assert( env_string );
|
||||||
|
if ( 0 != putenv( env_string ) ) {
|
||||||
|
perror( "putenv" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check again */
|
||||||
|
set_native_locale();
|
||||||
|
if ( !is_utf8_locale() ) {
|
||||||
|
fprintf( stderr, "mosh-server needs a UTF-8 native locale to run.\n\n" );
|
||||||
|
fprintf( stderr, "Unfortunately, the locale environment variables currently specify\nthe character set \"%s\".\n\n", locale_charset() );
|
||||||
|
int unused __attribute((unused)) = system( "locale" );
|
||||||
|
exit( 1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return run_server( desired_ip, desired_port, command, colors, verbose );
|
return run_server( desired_ip, desired_port, command, colors, verbose );
|
||||||
|
|||||||
@@ -49,7 +49,12 @@
|
|||||||
|
|
||||||
void STMClient::init( void )
|
void STMClient::init( void )
|
||||||
{
|
{
|
||||||
assert_utf8_locale();
|
if ( !is_utf8_locale() ) {
|
||||||
|
fprintf( stderr, "mosh-client needs a UTF-8 native locale to run.\n\n" );
|
||||||
|
fprintf( stderr, "Unfortunately, the locale environment variables currently specify\nthe character set \"%s\".\n\n", locale_charset() );
|
||||||
|
int unused __attribute((unused)) = system( "locale" );
|
||||||
|
exit( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
/* Verify terminal configuration */
|
/* Verify terminal configuration */
|
||||||
if ( tcgetattr( STDIN_FILENO, &saved_termios ) < 0 ) {
|
if ( tcgetattr( STDIN_FILENO, &saved_termios ) < 0 ) {
|
||||||
|
|||||||
@@ -29,19 +29,50 @@
|
|||||||
|
|
||||||
#include "locale_utils.h"
|
#include "locale_utils.h"
|
||||||
|
|
||||||
void assert_utf8_locale( void ) {
|
const char *locale_charset( void )
|
||||||
/* Verify locale calls for UTF-8 */
|
{
|
||||||
if ( strcmp( nl_langinfo( CODESET ), "UTF-8" ) != 0 &&
|
static const char ASCII_name[] = "US-ASCII (ANSI_X3.4-1968)";
|
||||||
strcmp( nl_langinfo( CODESET ), "utf-8" ) != 0 ) {
|
|
||||||
fprintf( stderr, "mosh requires a UTF-8 locale.\n" );
|
/* Produce more pleasant name of US-ASCII */
|
||||||
exit( 1 );
|
const char *ret = nl_langinfo( CODESET );
|
||||||
|
|
||||||
|
if ( strcmp( ret, "ANSI_X3.4-1968" ) == 0 ) {
|
||||||
|
ret = ASCII_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_utf8_locale( void ) {
|
||||||
|
/* Verify locale calls for UTF-8 */
|
||||||
|
if ( strcmp( locale_charset(), "UTF-8" ) != 0 &&
|
||||||
|
strcmp( locale_charset(), "utf-8" ) != 0 ) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_native_locale( void ) {
|
void set_native_locale( void ) {
|
||||||
/* Adopt native locale */
|
/* Adopt native locale */
|
||||||
if ( NULL == setlocale( LC_ALL, "" ) ) {
|
if ( NULL == setlocale( LC_ALL, "" ) ) {
|
||||||
perror( "setlocale" );
|
perror( "setlocale" );
|
||||||
exit( 1 );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void clear_locale_variables( void ) {
|
||||||
|
unsetenv( "LANG" );
|
||||||
|
unsetenv( "LANGUAGE" );
|
||||||
|
unsetenv( "LC_CTYPE" );
|
||||||
|
unsetenv( "LC_NUMERIC" );
|
||||||
|
unsetenv( "LC_TIME" );
|
||||||
|
unsetenv( "LC_COLLATE" );
|
||||||
|
unsetenv( "LC_MONETARY" );
|
||||||
|
unsetenv( "LC_MESSAGES" );
|
||||||
|
unsetenv( "LC_PAPER" );
|
||||||
|
unsetenv( "LC_NAME" );
|
||||||
|
unsetenv( "LC_ADDRESS" );
|
||||||
|
unsetenv( "LC_TELEPHONE" );
|
||||||
|
unsetenv( "LC_MEASUREMENT" );
|
||||||
|
unsetenv( "LC_IDENTIFICATION" );
|
||||||
|
unsetenv( "LC_ALL" );
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,7 +19,9 @@
|
|||||||
#ifndef LOCALE_UTILS_HPP
|
#ifndef LOCALE_UTILS_HPP
|
||||||
#define LOCALE_UTILS_HPP
|
#define LOCALE_UTILS_HPP
|
||||||
|
|
||||||
void assert_utf8_locale( void );
|
const char *locale_charset( void );
|
||||||
|
bool is_utf8_locale( void );
|
||||||
void set_native_locale( void );
|
void set_native_locale( void );
|
||||||
|
void clear_locale_variables( void );
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user