The patch add the following functions:
- newlocale(3)
- duplocale(3)
- freelocale(3)
- uselocale(3)
Documentation is missing for now. A separate patch will be provided.
uselocale(3):
a locale state is installed only once per-thread. If the user pass the
same locale_t object multiple times, the object will be duplicated. It
simplifies the sharing management (there are no sharing), and it seems
comformant to POSIX (for me at least).
> The uselocale() function shall set the current locale for the current
> thread to the locale represented by newloc.
The locale represented by newloc is effectively used: the duplication
don't change the "representation", that is what the user expects to be
installed.
But comments would be welcome !
newlocale(3):
The function is used to create a new locale state (not installed at
this stage), from a "base" (a model).
POSIX specifies that "it is unspecified" if the "base" locale_t is
modified or freed (and new state based on it created).
The current implementation create a new object (malloc) at each call,
and free the "base" locale. This behaviour is more simple to manage
the case where "base" is in already installed state (see freelocale(3)).
The initialisation of a new locale state is done by calling
loadlocale() for each categories, in the same manner setlocale(3) do
it for global state.
duplocale(3):
The implementation is similar to newlocale(3): we call loadlocale()
for each categories, following the categories defined in the locale to
be duplicated.
freelocale(3):
POSIX specifies an undefined behavior for freeing NULL (not a locale_t
object), LC_GLOBAL_LOCALE or used locale: we discard the free for any
of these pointers.
The free operation is somehow "special": we reset the locale value for
each categories to "C" locale. Doing that, loadlocale() function will
take care to release any resources (as when calling from
setlocale(3)). The resulting state will be pointers to statically
defined C categories that shouldn't be touched.
So, after reseting, just free() the locale state is enought.
--
Sebastien Marie
Index: b/include/locale.h
===================================================================
--- a/include/locale.h 2015-07-12 16:18:14.994338422 +0200
+++ b/include/locale.h 2015-07-12 16:21:38.775929858 +0200
@@ -95,6 +95,20 @@
extern struct _locale_t _lc_global_locale;
#define LC_GLOBAL_LOCALE (&_lc_global_locale)
+#define LC_CTYPE_MASK (1 << 0)
+#define LC_NUMERIC_MASK (1 << 1)
+#define LC_TIME_MASK (1 << 2)
+#define LC_COLLATE_MASK (1 << 3)
+#define LC_MONETARY_MASK (1 << 4)
+#define LC_MESSAGES_MASK (1 << 5)
+
+#define LC_ALL_MASK (0xff)
+
+locale_t newlocale(int, const char *, locale_t);
+locale_t duplocale(locale_t);
+void freelocale(locale_t);
+locale_t uselocale(locale_t);
+
#endif /* __POSIX_VISIBLE >= 200809 */
__END_DECLS
Index: b/lib/libc/locale/xlocale.c
===================================================================
--- a/lib/libc/locale/xlocale.c 2015-07-12 16:18:14.994338422 +0200
+++ b/lib/libc/locale/xlocale.c 2015-07-12 16:21:38.775929858 +0200
@@ -80,6 +80,36 @@
}
+struct _locale_t *
+uselocale(struct _locale_t * newloc)
+{
+ struct _locale_t * oldloc = NULL;
+
+ /* query mode */
+ if (newloc == NULL)
+ return (_current_locale());
+
+ /* reset to global locale */
+ if (newloc == &_lc_global_locale)
+ newloc = NULL;
+
+ /* a locale is installed once: duplicate it */
+ if (xlocale_is_installed(newloc->flags))
+ if ((newloc = duplocale(newloc)) == NULL)
+ return (NULL);
+
+ /* install the new locale */
+ newloc->flags |= XLOCALE_INSTALLED;
+ oldloc = _set_current_locale(newloc);
+
+ /* clear install flag on old locale */
+ if (oldloc != &_lc_global_locale)
+ oldloc->flags &= ~XLOCALE_INSTALLED;
+
+ return (oldloc);
+}
+
+
/* exported via ctype.h */
const char *
_ctype_(void)
Index: b/lib/libc/locale/setlocale.c
===================================================================
--- a/lib/libc/locale/setlocale.c 2015-07-12 16:18:55.314603522 +0200
+++ b/lib/libc/locale/setlocale.c 2015-07-12 17:46:36.429343448 +0200
@@ -32,6 +32,7 @@
*/
#include <sys/localedef.h>
+#include <errno.h>
#include <locale.h>
#include <limits.h>
#include <paths.h>
@@ -300,3 +301,110 @@
return env;
}
+
+
+struct _locale_t *
+newlocale(int category_mask, const char *locale, struct _locale_t *base)
+{
+ int i, loadlocale_success;
+ struct _locale_t *newloc = NULL;
+
+ /* default value for base */
+ if (base == NULL)
+ base = &_lc_global_locale;
+
+ if ((newloc = malloc(sizeof(struct _locale_t))) == NULL)
+ /* errno from malloc */
+ return (NULL);
+
+ /* flags (not installed, no cache) */
+ newloc->flags = 0;
+
+ /* load the locale for each categories */
+ loadlocale_success = 0;
+ for (i=1; i < _LC_LAST; i++)
+ if (((i == LC_COLLATE) && (category_mask & LC_COLLATE_MASK))
+ || ((i == LC_CTYPE) && (category_mask & LC_CTYPE_MASK))
+ || ((i == LC_MONETARY) && (category_mask &LC_MONETARY_MASK))
+ || ((i == LC_NUMERIC) && (category_mask & LC_NUMERIC_MASK))
+ || ((i == LC_TIME) && (category_mask & LC_MESSAGES_MASK))) {
+
+ if (loadlocale(i, locale, newloc) != NULL)
+ loadlocale_success = 1;
+
+ } else {
+ if (loadlocale(i, base->categories[i], newloc) != NULL)
+ loadlocale_success = 1;
+ }
+
+ /* failed if all categories failed */
+ if (loadlocale_success == 0) {
+ free(newloc);
+ return (NULL);
+ }
+
+ /* free base (_lc_global_locale is discarded by freelocale) */
+ freelocale(base);
+
+ return (newloc);
+}
+
+
+struct _locale_t *
+duplocale(struct _locale_t *loc)
+{
+ int i, loadlocale_success;
+ struct _locale_t *newloc = NULL;
+
+ /* check argument */
+ if (loc == NULL) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ /* alloc */
+ if ((newloc = malloc(sizeof(struct _locale_t))) == NULL)
+ /* errno from malloc */
+ return (NULL);
+
+ /* flags */
+ newloc->flags = 0;
+
+ /* load the locale for each categories */
+ loadlocale_success = 0;
+ for (i=1; i < _LC_LAST; i++)
+ if (loadlocale(i, loc->categories[i], newloc) != NULL)
+ loadlocale_success = 1;
+
+ /* failed if all categories failed */
+ if (loadlocale_success == 0) {
+ free(newloc);
+ newloc = NULL;
+ }
+
+ return (newloc);
+}
+
+
+void
+freelocale(struct _locale_t *loc)
+{
+ int i;
+
+ /*
+ * discard on:
+ * - NULL
+ * - attempt to free global locale
+ * - attempt to free an currently installed locale
+ */
+ if ((loc == NULL)
+ || (loc == &_lc_global_locale)
+ || xlocale_is_installed(loc->flags))
+ return;
+
+ /* reset to default values */
+ for (i=1; i < _LC_LAST; i++)
+ loadlocale(i, "C", loc);
+
+ free(loc);
+}