From: Bruno Haible Date: Wed, 23 Dec 2009 14:23:48 +0000 (+0100) Subject: localename: Make aware of thread locale. X-Git-Tag: v0.1~5003 X-Git-Url: http://erislabs.net/gitweb/?a=commitdiff_plain;h=85de14f614534603cf01144192cae044df638e22;p=gnulib.git localename: Make aware of thread locale. --- diff --git a/ChangeLog b/ChangeLog index 97e496b0e..93bfdf371 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,23 @@ +2009-12-23 Bruno Haible + + localename: Make aware of thread locale. + * lib/localename.h (gl_locale_name_thread): New declaration. + (gl_locale_name, gl_locale_name_posix, gl_locale_name_default): Clarify + behaviour with respect to thread locale. + * lib/localename.c: Include , , , + , glthread/lock.h. + (SIZE_BITS): New macro. + (string_hash): New function. + (struct hash_node): New type. + (HASH_TABLE_SIZE): New macro. + (struniq_hash_table, struniq_lock): New variables. + (struniq): New function. + (gl_locale_name_thread): New function. + (gl_locale_name): Invoke it. + * m4/localename.m4 (gl_LOCALENAME): Test for uselocale function. + * modules/localename (Depends-on): Add lock. + Reported by Mike Gran . + 2009-12-23 Eric Blake va-args: new module diff --git a/lib/localename.c b/lib/localename.c index ceed70f52..4b77c95fa 100644 --- a/lib/localename.c +++ b/lib/localename.c @@ -29,10 +29,23 @@ # include "localename.h" #endif +#include +#include #include #include #include +#if HAVE_USELOCALE +/* MacOS X 10.5 defines the locale_t type in . */ +# if defined __APPLE__ && defined __MACH__ +# include +# endif +# include +# if !defined IN_LIBINTL +# include "glthread/lock.h" +# endif +#endif + #if HAVE_CFLOCALECOPYCURRENT || HAVE_CFPREFERENCESCOPYAPPVALUE # include # if HAVE_CFLOCALECOPYCURRENT @@ -2494,6 +2507,216 @@ gl_locale_name_from_win32_LCID (LCID lcid) #endif +#if defined __APPLE__ && defined __MACH__ && HAVE_USELOCALE /* MacOS X */ + +/* Simple hash set of strings. We don't want to drag in lots of hash table + code here. */ + +# define SIZE_BITS (sizeof (size_t) * CHAR_BIT) + +/* A hash function for NUL-terminated char* strings using + the method described by Bruno Haible. + See http://www.haible.de/bruno/hashfunc.html. */ +static size_t +string_hash (const void *x) +{ + const char *s = (const char *) x; + size_t h = 0; + + for (; *s; s++) + h = *s + ((h << 9) | (h >> (SIZE_BITS - 9))); + + return h; +} + +/* A hash table of fixed size. Multiple threads can access it read-only + simultaneously, but only one thread can insert into it at the same time. */ + +/* A node in a hash bucket collision list. */ +struct hash_node + { + struct hash_node * volatile next; + char contents[100]; /* has variable size */ + }; + +# define HASH_TABLE_SIZE 257 +static struct hash_node * volatile struniq_hash_table[HASH_TABLE_SIZE] + /* = { NULL, ..., NULL } */; + +/* This lock protects the struniq_hash_table against multiple simultaneous + insertions. */ +gl_lock_define_initialized(static, struniq_lock) + +/* Store a copy of the given string in a string pool with indefinite extent. + Return a pointer to this copy. */ +static const char * +struniq (const char *string) +{ + size_t hashcode = string_hash (string); + size_t slot = hashcode % HASH_TABLE_SIZE; + size_t size; + struct hash_node *new_node; + struct hash_node *p; + for (p = struniq_hash_table[slot]; p != NULL; p = p->next) + if (strcmp (p->contents, string) == 0) + return p->contents; + size = strlen (string) + 1; + new_node = + (struct hash_node *) + malloc (offsetof (struct hash_node, contents[0]) + size); + if (new_node == NULL) + /* Out of memory. Return a statically allocated string. */ + return "C"; + memcpy (new_node->contents, string, size); + /* Lock while inserting new_node. */ + gl_lock_lock (struniq_lock); + /* Check whether another thread already added the string while we were + waiting on the lock. */ + for (p = struniq_hash_table[slot]; p != NULL; p = p->next) + if (strcmp (p->contents, string) == 0) + { + free (new_node); + new_node = p; + goto done; + } + /* Really insert new_node into the hash table. Fill new_node entirely first, + because other threads may be iterating over the linked list. */ + new_node->next = struniq_hash_table[slot]; + struniq_hash_table[slot] = new_node; + done: + /* Unlock after new_node is inserted. */ + gl_lock_unlock (struniq_lock); + return new_node->contents; +} + +#endif + + +const char * +gl_locale_name_thread (int category, const char *categoryname) +{ +#if HAVE_USELOCALE + { + locale_t thread_locale = uselocale (NULL); + if (thread_locale != LC_GLOBAL_LOCALE) + { +# if __GLIBC__ >= 2 + /* Work around an incorrect definition of the _NL_LOCALE_NAME macro in + glibc < 2.12. + See . */ + const char *name = + nl_langinfo (_NL_ITEM ((category), _NL_ITEM_INDEX (-1))); + if (name[0] == '\0') + /* Fallback code for glibc < 2.4, which did not implement + nl_langinfo (_NL_LOCALE_NAME (category)). */ + name = thread_locale->__names[category]; + return name; +# endif +# if defined __APPLE__ && defined __MACH__ /* MacOS X */ + /* The locale name is found deep in an undocumented data structure. + Since it's stored in a buffer of size 32 and newlocale() rejects + locale names of length > 31, we can assume that it is NUL terminated + in this buffer. But we need to make a copy of the locale name, of + indefinite extent. */ + struct _xlocale + { + int32_t __refcount; + void (*__free_extra)(void *); + __darwin_mbstate_t __mbs[10]; + int64_t __magic; + unsigned char __collate_load_error; + unsigned char __collate_substitute_nontrivial; + unsigned char _messages_using_locale; + unsigned char _monetary_using_locale; + unsigned char _numeric_using_locale; + unsigned char _time_using_locale; + unsigned char __mlocale_changed; + unsigned char __nlocale_changed; + unsigned char __numeric_fp_cvt; + struct __xlocale_st_collate *__lc_collate; + struct __xlocale_st_runelocale *__lc_ctype; + struct __xlocale_st_messages *__lc_messages; + struct __xlocale_st_monetary *__lc_monetary; + struct __xlocale_st_numeric *__lc_numeric; + struct _xlocale *__lc_numeric_loc; + struct __xlocale_st_time *__lc_time; + /* more */ + }; + struct __xlocale_st_collate + { + int32_t __refcount; + void (*__free_extra)(void *); + char __encoding[32]; + /* more */ + }; + struct __xlocale_st_runelocale + { + int32_t __refcount; + void (*__free_extra)(void *); + char __ctype_encoding[32]; + /* more */ + }; + struct __xlocale_st_messages + { + int32_t __refcount; + void (*__free_extra)(void *); + char *_messages_locale_buf; + /* more */ + }; + struct __xlocale_st_monetary + { + int32_t __refcount; + void (*__free_extra)(void *); + char *_monetary_locale_buf; + /* more */ + }; + struct __xlocale_st_numeric { + int32_t __refcount; + void (*__free_extra)(void *); + char *_numeric_locale_buf; + /* more */ + }; + struct __xlocale_st_time { + int32_t __refcount; + void (*__free_extra)(void *); + char *_time_locale_buf; + /* more */ + }; + struct _xlocale *tlp = (struct _xlocale *) thread_locale; + switch (category) + { + case LC_CTYPE: + return struniq (tlp->__lc_ctype->__ctype_encoding); + case LC_NUMERIC: + return tlp->_numeric_using_locale + ? struniq (tlp->__lc_numeric->_numeric_locale_buf) + : "C"; + case LC_TIME: + return tlp->_time_using_locale + ? struniq (tlp->__lc_time->_time_locale_buf) + : "C"; + case LC_COLLATE: + return !tlp->__collate_load_error + ? struniq (tlp->__lc_collate->__encoding) + : "C"; + case LC_MONETARY: + return tlp->_monetary_using_locale + ? struniq (tlp->__lc_monetary->_monetary_locale_buf) + : "C"; + case LC_MESSAGES: + return tlp->_messages_using_locale + ? struniq (tlp->__lc_messages->_messages_locale_buf) + : "C"; + default: /* We shouldn't get here. */ + return ""; + } +# endif + } + } +#endif + return NULL; +} + /* XPG3 defines the result of 'setlocale (category, NULL)' as: "Directs 'setlocale()' to query 'category' and return the current setting of 'local'." @@ -2504,12 +2727,6 @@ gl_locale_name_from_win32_LCID (LCID lcid) # define HAVE_LOCALE_NULL #endif -/* Determine the current locale's name, and canonicalize it into XPG syntax - language[_territory][.codeset][@modifier] - The codeset part in the result is not reliable; the locale_charset() - should be used for codeset information instead. - The result must not be freed; it is statically allocated. */ - const char * gl_locale_name_posix (int category, const char *categoryname) { @@ -2670,11 +2887,21 @@ gl_locale_name_default (void) #endif } +/* Determine the current locale's name, and canonicalize it into XPG syntax + language[_territory][.codeset][@modifier] + The codeset part in the result is not reliable; the locale_charset() + should be used for codeset information instead. + The result must not be freed; it is statically allocated. */ + const char * gl_locale_name (int category, const char *categoryname) { const char *retval; + retval = gl_locale_name_thread (category, categoryname); + if (retval != NULL) + return retval; + retval = gl_locale_name_posix (category, categoryname); if (retval != NULL) return retval; diff --git a/lib/localename.h b/lib/localename.h index 312a7936a..2413d46e0 100644 --- a/lib/localename.h +++ b/lib/localename.h @@ -25,9 +25,9 @@ extern "C" { /* Determine the current locale's name. - It considers both the POSIX notion of locale name (see function - gl_locale_name_posix) and the system notion of locale name (see function - gl_locale_name_default). + It considers both the POSIX notion of locale name (see functions + gl_locale_name_thread and gl_locale_name_posix) and the system notion + of locale name (see function gl_locale_name_default). CATEGORY is a locale category abbreviation, as defined in , but not LC_ALL. E.g. LC_MESSAGES. CATEGORYNAME is the name of CATEGORY as a string, e.g. "LC_MESSAGES". @@ -38,8 +38,21 @@ extern "C" { The result must not be freed; it is statically allocated. */ extern const char * gl_locale_name (int category, const char *categoryname); -/* Determine the current locale's name, as specified by setlocale() calls - or by environment variables. +/* Determine the current per-thread locale's name, as specified by uselocale() + calls. + CATEGORY is a locale category abbreviation, as defined in , + but not LC_ALL. E.g. LC_MESSAGES. + CATEGORYNAME is the name of CATEGORY as a string, e.g. "LC_MESSAGES". + Return the locale category's name, canonicalized into XPG syntax + language[_territory][.codeset][@modifier] + or NULL if no locale has been specified for the current thread. + The codeset part in the result is not reliable; the locale_charset() + should be used for codeset information instead. + The result must not be freed; it is statically allocated. */ +extern const char * gl_locale_name_thread (int category, const char *categoryname); + +/* Determine the thread-independent current locale's name, as specified by + setlocale() calls or by environment variables. CATEGORY is a locale category abbreviation, as defined in , but not LC_ALL. E.g. LC_MESSAGES. CATEGORYNAME is the name of CATEGORY as a string, e.g. "LC_MESSAGES". @@ -60,9 +73,9 @@ extern const char * gl_locale_name_posix (int category, const char *categoryname extern const char * gl_locale_name_environ (int category, const char *categoryname); /* Determine the default locale's name. This is the current locale's name, - if not specified by setlocale() calls or by environment variables. This - locale name is usually determined by systems settings that the user can - manipulate through a GUI. + if not specified by uselocale() calls, by setlocale() calls, or by + environment variables. This locale name is usually determined by systems + settings that the user can manipulate through a GUI. Quoting POSIX:2001: "All implementations shall define a locale as the default locale, diff --git a/m4/localename.m4 b/m4/localename.m4 index 94e9705db..c5c630008 100644 --- a/m4/localename.m4 +++ b/m4/localename.m4 @@ -1,5 +1,5 @@ -# localename.m4 serial 1 -dnl Copyright (C) 2007 Free Software Foundation, Inc. +# localename.m4 serial 2 +dnl Copyright (C) 2007, 2009 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. @@ -8,5 +8,5 @@ AC_DEFUN([gl_LOCALENAME], [ AC_REQUIRE([gt_LC_MESSAGES]) AC_REQUIRE([gt_INTL_MACOSX]) - AC_CHECK_FUNCS([setlocale]) + AC_CHECK_FUNCS([setlocale uselocale]) ]) diff --git a/modules/localename b/modules/localename index 0d9f05e2e..165de3524 100644 --- a/modules/localename +++ b/modules/localename @@ -10,6 +10,7 @@ m4/lcmessage.m4 Depends-on: strdup +lock configure.ac: gl_LOCALENAME