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);
+}

Reply via email to