diff --git a/common.c b/common.c index 746b829c2..9a12d41de 100644 --- a/common.c +++ b/common.c @@ -104,8 +104,17 @@ static struct winsize termsize; */ static int block_count=0; +static string_buffer_t *setlocale_buff=0; + + 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 ); } @@ -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 ); + /* 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'$'; - } - else - { - char *lang = getenv( "LANG" ); - if( lang ) - ellipsis_char = strstr( lang, ".UTF")?L'\u2026':L'$'; - else - ellipsis_char = L'$'; + setlocale_buff = malloc( sizeof(string_buffer_t) ); + sb_init( setlocale_buff); } + + sb_clear( setlocale_buff ); + sb_printf( setlocale_buff, L"%s", res ); + + return (wchar_t *)setlocale_buff->buff; } int contains_str( const wchar_t *a, ... ) diff --git a/common.h b/common.h index 77de61ca3..33eb8341b 100644 --- a/common.h +++ b/common.h @@ -256,9 +256,12 @@ wchar_t *quote_end( const wchar_t *in ); 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 diff --git a/doc_src/doc.hdr b/doc_src/doc.hdr index 3795eb738..8117458c5 100644 --- a/doc_src/doc.hdr +++ b/doc_src/doc.hdr @@ -655,6 +655,7 @@ values of these variables. They are: - \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 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 Locale variables for more information. 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 @@ -663,6 +664,22 @@ unexported variables. \c fish also uses several variables internally. Such variables are prefixed with the string __FISH or __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 Many other shells have a large library of builtin commands. Most of diff --git a/env.c b/env.c index e666ce485..ac52ee453 100644 --- a/env.c +++ b/env.c @@ -214,6 +214,73 @@ static mode_t get_umask() 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 proper events are triggered when an event occurs. @@ -224,6 +291,9 @@ static void universal_callback( int type, { wchar_t *str=0; + if( is_locale( name ) ) + handle_locale(); + switch( type ) { case SET: @@ -475,7 +545,7 @@ static env_node_t *env_get_node( const wchar_t *key ) return 0; } - + void env_set( const wchar_t *key, const wchar_t *val, int var_mode ) @@ -490,26 +560,13 @@ void env_set( const wchar_t *key, event_t ev; int is_universal = 0; - + if( (var_mode & ENV_USER ) && hash_get( &env_read_only, key ) ) { 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) { wchar_t *end; @@ -533,7 +590,6 @@ void env_set( const wchar_t *key, */ return; } - /* 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" ); al_destroy( &ev.arguments ); } - + + if( is_locale( key ) ) + { + handle_locale(); + } + } + + + /** Attempt to remove/free the specified key/value pair from the specified hash table. @@ -735,6 +799,10 @@ void env_remove( const wchar_t *key, int var_mode ) { env_universal_remove( key ); } + + if( is_locale( key ) ) + handle_locale(); + } diff --git a/fish_pager.c b/fish_pager.c index 9131e7913..6669a04e0 100644 --- a/fish_pager.c +++ b/fish_pager.c @@ -845,8 +845,7 @@ static void init() { struct sigaction act; program_name = L"fish_pager"; - fish_setlocale( LC_ALL, L"" ); - + wsetlocale( LC_ALL, L"" ); int out = dup( 1 ); close(1); diff --git a/fishd.c b/fishd.c index c9f5efa76..9b29fd20c 100644 --- a/fishd.c +++ b/fishd.c @@ -400,7 +400,7 @@ static void init() sock = get_socket(); daemonize(); - fish_setlocale( LC_ALL, L"" ); + wsetlocale( LC_ALL, L"" ); env_universal_common_init( &broadcast ); load(); diff --git a/parser.c b/parser.c index 87bb292eb..63b06ce4d 100644 --- a/parser.c +++ b/parser.c @@ -2114,7 +2114,7 @@ static void eval_job( tokenizer *tok ) tok_get_desc( tok_last_type(tok) ) ); } - return 0; + return; } case TOK_ERROR: