Improve locale implementation (should now behave identically to bash) and document locale variables behaviour

darcs-hash:20060108230049-ac50b-403f1d00c8483fc4fecc275b62e40b1c3d51bfc1.gz
This commit is contained in:
axel 2006-01-09 09:00:49 +10:00
parent 690648e1b0
commit 906495d713
7 changed files with 138 additions and 36 deletions

View file

@ -104,8 +104,17 @@ static struct winsize termsize;
*/ */
static int block_count=0; static int block_count=0;
static string_buffer_t *setlocale_buff=0;
void common_destroy() void common_destroy()
{ {
if( setlocale_buff )
{
sb_destroy( setlocale_buff );
free( setlocale_buff );
}
debug( 3, L"Calls: wcsdupcat %d, wcsdupcat2 %d, wcsndup %d, str2wcs %d, wcs2str %d", c1, c2, c3, c4, c5 ); debug( 3, L"Calls: wcsdupcat %d, wcsdupcat2 %d, wcsndup %d, str2wcs %d, wcs2str %d", c1, c2, c3, c4, c5 );
} }
@ -719,27 +728,33 @@ wchar_t *quote_end( const wchar_t *in )
} }
void fish_setlocale(int category, const wchar_t *locale) const wchar_t *wsetlocale(int category, const wchar_t *locale)
{ {
char *lang = wcs2str( locale );
setlocale(category,lang); char *lang = locale?wcs2str( locale ):0;
char * res = setlocale(category,lang);
free( lang ); free( lang );
/* /*
Use ellipsis if on known unicode system, otherwise use $ Use ellipsis if on known unicode system, otherwise use $
*/ */
if( wcslen( locale ) ) char *ctype = setlocale( LC_CTYPE, (void *)0 );
ellipsis_char = (strstr( ctype, ".UTF")||strstr( ctype, ".utf") )?L'\u2026':L'$';
if( !res )
return 0;
if( !setlocale_buff )
{ {
ellipsis_char = wcsstr( locale, L".UTF")?L'\u2026':L'$'; setlocale_buff = malloc( sizeof(string_buffer_t) );
} sb_init( setlocale_buff);
else
{
char *lang = getenv( "LANG" );
if( lang )
ellipsis_char = strstr( lang, ".UTF")?L'\u2026':L'$';
else
ellipsis_char = L'$';
} }
sb_clear( setlocale_buff );
sb_printf( setlocale_buff, L"%s", res );
return (wchar_t *)setlocale_buff->buff;
} }
int contains_str( const wchar_t *a, ... ) int contains_str( const wchar_t *a, ... )

View file

@ -256,9 +256,12 @@ wchar_t *quote_end( const wchar_t *in );
void error_reset(); void error_reset();
/** /**
Set the locale, also change the ellipsis character This function behaves exactly like a wide character equivalent of
the C function setlocale, except that it will also try to detect if
the user is using a Unicode character set, and if so, use the
unicode ellipsis character as ellipsis, instead of '$'.
*/ */
void fish_setlocale( int category, const wchar_t *locale ); const wchar_t *wsetlocale( int category, const wchar_t *locale );
/** /**
Checks if \c needle is included in the list of strings specified Checks if \c needle is included in the list of strings specified

View file

@ -655,6 +655,7 @@ values of these variables. They are:
- \c PWD, which is the current working directory. - \c PWD, which is the current working directory.
- \c status, which is the exit status of the last foreground job to exit. If a job contains pipelines, the status of the last command in the pipeline is the status for the job. - \c status, which is the exit status of the last foreground job to exit. If a job contains pipelines, the status of the last command in the pipeline is the status for the job.
- \c USER, which is the username. This variable can only be changed by the root user. - \c USER, which is the username. This variable can only be changed by the root user.
- \c LANG, \cLC_ALL, \c LC_COLLATE, \c LC_CTYPE, \cLC_MESSAGES, \c LC_MONETARY, \c LC_NUMERIC and LC_TIME set the language option for the shell and subprograms. See the section <a href='#variables-locale'>Locale variables</a> for more information.
Variables whose name are in uppercase are exported to the commands Variables whose name are in uppercase are exported to the commands
started by fish. This rule is not enforced by fish, but it is good started by fish. This rule is not enforced by fish, but it is good
@ -663,6 +664,22 @@ unexported variables. \c fish also uses several variables
internally. Such variables are prefixed with the string __FISH or internally. Such variables are prefixed with the string __FISH or
__fish. These should be ignored by the user. __fish. These should be ignored by the user.
\subsection variables-locale Locale variables
The most common way to set the locale to use a command like 'set -x
LANG en_GB.utf8', which sets the current locale to be the english
language, adapted to great britain, using the UTF-8 character set. For
a list of available locales, use 'locale -a'.
\c LANG, \cLC_ALL, \c LC_COLLATE, \c LC_CTYPE, \cLC_MESSAGES, \c
LC_MONETARY, \c LC_NUMERIC and LC_TIME set the language option for the
shell and subprograms. These variables work as follows: \c LC_ALL
forces all the aspects of the locale to the specified value. If LC_ALL
is set, all other locale variables will be ignored. The other LC_
variables set the specified aspect of the locale information. . LANG
is a fallback value, it will be used if none of the LC_ variables are
specified.
\section builtin-overview Builtins \section builtin-overview Builtins
Many other shells have a large library of builtin commands. Most of Many other shells have a large library of builtin commands. Most of

102
env.c
View file

@ -214,6 +214,73 @@ static mode_t get_umask()
return res; return res;
} }
/**
Checks if the specified variable is a locale variable
*/
static int is_locale( const wchar_t *key )
{
return contains_str( key, L"LANG", L"LC_ALL", L"LC_COLLATE", L"LC_CTYPE", L"LC_MESSAGES", L"LC_MONETARY", L"LC_NUMERIC", L"LC_TIME", (void *)0);
}
/**
Properly sets all locale information
*/
static void handle_locale()
{
const wchar_t *lc_all = env_get( L"LC_ALL" );
const wchar_t *lang;
int i;
wchar_t *old = wcsdup(wsetlocale( LC_MESSAGES, (void *)0 ));
static const wchar_t *lc[] =
{
L"LC_COLLATE", L"LC_CTYPE", L"LC_MESSAGES", L"LC_MONETARY", L"LC_NUMERIC", L"LC_TIME", (void *)0
}
;
static const int cat[] =
{
LC_COLLATE, LC_CTYPE, LC_MESSAGES, LC_MONETARY, LC_NUMERIC, LC_TIME
}
;
if( lc_all )
{
wsetlocale( LC_ALL, lc_all );
}
else
{
lang = env_get( L"LANG" );
if( lang )
{
wsetlocale( LC_ALL, lang );
}
for( i=0; lc[i]; i++ )
{
const wchar_t *val = env_get( lc[i] );
if( val )
wsetlocale( cat[i], val );
}
}
if( wcscmp( wsetlocale( LC_MESSAGES, (void *)0 ), old ) != 0 )
{
/* Make change known to gettext. */
{
extern int _nl_msg_cat_cntr;
++_nl_msg_cat_cntr;
}
if( is_interactive )
{
debug( 0, _(L"Changing language to english") );
}
}
free( old );
}
/** /**
Universal variable callback function. This function makes sure the Universal variable callback function. This function makes sure the
proper events are triggered when an event occurs. proper events are triggered when an event occurs.
@ -224,6 +291,9 @@ static void universal_callback( int type,
{ {
wchar_t *str=0; wchar_t *str=0;
if( is_locale( name ) )
handle_locale();
switch( type ) switch( type )
{ {
case SET: case SET:
@ -475,7 +545,7 @@ static env_node_t *env_get_node( const wchar_t *key )
return 0; return 0;
} }
void env_set( const wchar_t *key, void env_set( const wchar_t *key,
const wchar_t *val, const wchar_t *val,
int var_mode ) int var_mode )
@ -490,26 +560,13 @@ void env_set( const wchar_t *key,
event_t ev; event_t ev;
int is_universal = 0; int is_universal = 0;
if( (var_mode & ENV_USER ) && if( (var_mode & ENV_USER ) &&
hash_get( &env_read_only, key ) ) hash_get( &env_read_only, key ) )
{ {
return; return;
} }
if( wcscmp(key, L"LANG" )==0 )
{
fish_setlocale(LC_ALL,val);
/* Make change known to gettext. */
{
extern int _nl_msg_cat_cntr;
++_nl_msg_cat_cntr;
}
if( is_interactive )
debug( 0, _(L"Changing language to english") );
}
if( wcscmp( key, L"umask" ) == 0) if( wcscmp( key, L"umask" ) == 0)
{ {
wchar_t *end; wchar_t *end;
@ -533,7 +590,6 @@ void env_set( const wchar_t *key,
*/ */
return; return;
} }
/* /*
Zero element arrays are internaly not coded as null but as this placeholder string Zero element arrays are internaly not coded as null but as this placeholder string
@ -685,9 +741,17 @@ void env_set( const wchar_t *key,
// debug( 1, L"env_set: return from event firing" ); // debug( 1, L"env_set: return from event firing" );
al_destroy( &ev.arguments ); al_destroy( &ev.arguments );
} }
if( is_locale( key ) )
{
handle_locale();
}
} }
/** /**
Attempt to remove/free the specified key/value pair from the Attempt to remove/free the specified key/value pair from the
specified hash table. specified hash table.
@ -735,6 +799,10 @@ void env_remove( const wchar_t *key, int var_mode )
{ {
env_universal_remove( key ); env_universal_remove( key );
} }
if( is_locale( key ) )
handle_locale();
} }

View file

@ -845,8 +845,7 @@ static void init()
{ {
struct sigaction act; struct sigaction act;
program_name = L"fish_pager"; program_name = L"fish_pager";
fish_setlocale( LC_ALL, L"" ); wsetlocale( LC_ALL, L"" );
int out = dup( 1 ); int out = dup( 1 );
close(1); close(1);

View file

@ -400,7 +400,7 @@ static void init()
sock = get_socket(); sock = get_socket();
daemonize(); daemonize();
fish_setlocale( LC_ALL, L"" ); wsetlocale( LC_ALL, L"" );
env_universal_common_init( &broadcast ); env_universal_common_init( &broadcast );
load(); load();

View file

@ -2114,7 +2114,7 @@ static void eval_job( tokenizer *tok )
tok_get_desc( tok_last_type(tok) ) ); tok_get_desc( tok_last_type(tok) ) );
} }
return 0; return;
} }
case TOK_ERROR: case TOK_ERROR: