Print this page
2964 need POSIX 2008 locale object support
Reviewed by: Robert Mustacchi <rm@joyent.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Approved by: TBD

Split Close
Expand all
Collapse all
          --- old/usr/src/lib/libc/port/locale/setlocale.c
          +++ new/usr/src/lib/libc/port/locale/setlocale.c
   1    1  /*
        2 + * Copyright 2014 Garrett D'Amore <garrett@damore.org>
   2    3   * Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
   3    4   * Copyright (c) 1996 - 2002 FreeBSD Project
   4    5   * Copyright (c) 1991, 1993
   5    6   *      The Regents of the University of California.  All rights reserved.
   6    7   *
   7    8   * This code is derived from software contributed to Berkeley by
   8    9   * Paul Borman at Krystal Technologies.
   9   10   *
  10   11   * Redistribution and use in source and binary forms, with or without
  11   12   * modification, are permitted provided that the following conditions
↓ open down ↓ 22 lines elided ↑ open up ↑
  34   35  
  35   36  #include "lint.h"
  36   37  #include <sys/types.h>
  37   38  #include <sys/stat.h>
  38   39  #include <errno.h>
  39   40  #include <limits.h>
  40   41  #include <locale.h>
  41   42  #include <stdlib.h>
  42   43  #include <string.h>
  43   44  #include <unistd.h>
  44      -#include <alloca.h>
  45   45  #include <stdio.h>
       46 +#include "mtlib.h"
  46   47  #include "collate.h"
  47      -#include "lmonetary.h"  /* for __monetary_load_locale() */
  48      -#include "lnumeric.h"   /* for __numeric_load_locale() */
  49      -#include "lmessages.h"  /* for __messages_load_locale() */
       48 +#include "lnumeric.h"   /* for struct lc_numeric */
       49 +#include "lctype.h"     /* for struct lc_ctype */
  50   50  #include "setlocale.h"
  51      -#include "ldpart.h"
  52      -#include "timelocal.h" /* for __time_load_locale() */
  53   51  #include "../i18n/_loc_path.h"
       52 +#include "localeimpl.h"
       53 +#include "../i18n/_locale.h"
  54   54  
  55   55  /*
  56      - * Category names for getenv()  Note that this was modified
  57      - * for Solaris.  See <iso/locale_iso.h>.
  58      - */
  59      -#define NUM_CATS        7
  60      -static char *categories[7] = {
  61      -        "LC_CTYPE",
  62      -        "LC_NUMERIC",
  63      -        "LC_TIME",
  64      -        "LC_COLLATE",
  65      -        "LC_MONETARY",
  66      -        "LC_MESSAGES",
  67      -        "LC_ALL",
  68      -};
  69      -
  70      -/*
  71      - * Current locales for each category
  72      - */
  73      -static char current_categories[NUM_CATS][ENCODING_LEN + 1] = {
  74      -        "C",
  75      -        "C",
  76      -        "C",
  77      -        "C",
  78      -        "C",
  79      -        "C",
  80      -        "C",
  81      -};
  82      -
  83      -/*
  84   56   * Path to locale storage directory.  See ../i18n/_loc_path.h
  85   57   */
  86   58  char    *_PathLocale = _DFLT_LOC_PATH;
  87   59  
  88      -/*
  89      - * The locales we are going to try and load
  90      - */
  91      -static char new_categories[NUM_CATS][ENCODING_LEN + 1];
  92      -static char saved_categories[NUM_CATS][ENCODING_LEN + 1];
  93      -static char current_locale_string[NUM_CATS * (ENCODING_LEN + 1 + 1)];
       60 +static char     *current_locale(locale_t, int);
       61 +static void     install_legacy(locale_t, int);
  94   62  
  95      -static char     *currentlocale(void);
  96      -static char     *loadlocale(int);
  97      -static const char *__get_locale_env(int);
       63 +static mutex_t setlocale_lock = DEFAULTMUTEX;
       64 +static locale_t setlocale_list = NULL;
  98   65  
  99   66  char *
 100      -setlocale(int category, const char *locale)
       67 +setlocale(int category, const char *locname)
 101   68  {
 102      -        int i, j, saverr;
 103      -        const char *env, *r;
       69 +        locale_t loc;
       70 +        locale_t srch;
       71 +        int mask;
 104   72  
 105      -        if (category < 0 || category >= NUM_CATS) {
       73 +        if (category < 0 || category > LC_ALL) {
 106   74                  errno = EINVAL;
 107   75                  return (NULL);
 108   76          }
 109   77  
 110      -        if (locale == NULL)
 111      -                return (category != LC_ALL ?
 112      -                    current_categories[category] : currentlocale());
       78 +        if (locname == NULL)
       79 +                return (current_locale(___global_locale, category));
 113   80  
 114      -        /*
 115      -         * Default to the current locale for everything.
 116      -         */
 117      -        for (i = 0; i < NUM_CATS; ++i)
 118      -                (void) strcpy(new_categories[i], current_categories[i]);
       81 +        mask = (category == LC_ALL ? LC_ALL_MASK : (1 << category));
 119   82  
 120      -        /*
 121      -         * Now go fill up new_categories from the locale argument
 122      -         */
 123      -        if (!*locale) {
 124      -                if (category == LC_ALL) {
 125      -                        for (i = 0; i < NUM_CATS; ++i) {
 126      -                                if (i == LC_ALL)
 127      -                                        continue;
 128      -                                env = __get_locale_env(i);
 129      -                                if (strlen(env) > ENCODING_LEN) {
 130      -                                        errno = EINVAL;
 131      -                                        return (NULL);
 132      -                                }
 133      -                                (void) strcpy(new_categories[i], env);
 134      -                        }
 135      -                } else {
 136      -                        env = __get_locale_env(category);
 137      -                        if (strlen(env) > ENCODING_LEN) {
 138      -                                errno = EINVAL;
 139      -                                return (NULL);
 140      -                        }
 141      -                        (void) strcpy(new_categories[category], env);
 142      -                }
 143      -        } else if (category != LC_ALL) {
 144      -                if (strlen(locale) > ENCODING_LEN) {
 145      -                        errno = EINVAL;
 146      -                        return (NULL);
 147      -                }
 148      -                (void) strcpy(new_categories[category], locale);
 149      -        } else {
 150      -                if ((r = strchr(locale, '/')) == NULL) {
 151      -                        if (strlen(locale) > ENCODING_LEN) {
 152      -                                errno = EINVAL;
 153      -                                return (NULL);
 154      -                        }
 155      -                        for (i = 0; i < NUM_CATS; ++i)
 156      -                                (void) strcpy(new_categories[i], locale);
 157      -                } else {
 158      -                        char    *buf;
 159      -                        char    *save;
 160      -
 161      -                        buf = alloca(strlen(locale) + 1);
 162      -                        (void) strcpy(buf, locale);
 163      -
 164      -                        save = NULL;
 165      -                        r = strtok_r(buf, "/", &save);
 166      -                        for (i = 0;  i < NUM_CATS; i++) {
 167      -                                if (i == LC_ALL)
 168      -                                        continue;
 169      -                                if (r == NULL) {
 170      -                                        /*
 171      -                                         * Composite Locale is inadequately
 172      -                                         * specified!   (Or with empty fields.)
 173      -                                         * The old code would fill fields
 174      -                                         * out from the last one, but I think
 175      -                                         * this is suboptimal.
 176      -                                         */
 177      -                                        errno = EINVAL;
 178      -                                        return (NULL);
 179      -                                }
 180      -                                (void) strlcpy(new_categories[i], r,
 181      -                                    ENCODING_LEN);
 182      -                                r = strtok_r(NULL, "/", &save);
 183      -                        }
 184      -                        if (r != NULL) {
 185      -                                /*
 186      -                                 * Too many components - we had left over
 187      -                                 * data in the LC_ALL.  It is malformed.
 188      -                                 */
 189      -                                errno = EINVAL;
 190      -                                return (NULL);
 191      -                        }
 192      -                }
       83 +        loc = newlocale(mask, locname, NULL);
       84 +        if (loc == NULL) {
       85 +                return (NULL);
 193   86          }
 194   87  
 195      -        if (category != LC_ALL)
 196      -                return (loadlocale(category));
 197      -
 198      -        for (i = 0; i < NUM_CATS; ++i) {
 199      -                (void) strcpy(saved_categories[i], current_categories[i]);
 200      -                if (i == LC_ALL)
 201      -                        continue;
 202      -                if (loadlocale(i) == NULL) {
 203      -                        saverr = errno;
 204      -                        for (j = 0; j < i; j++) {
 205      -                                (void) strcpy(new_categories[j],
 206      -                                    saved_categories[j]);
 207      -                                if (i == LC_ALL)
 208      -                                        continue;
 209      -                                if (loadlocale(j) == NULL) {
 210      -                                        (void) strcpy(new_categories[j], "C");
 211      -                                        (void) loadlocale(j);
 212      -                                }
 213      -                        }
 214      -                        errno = saverr;
 215      -                        return (NULL);
 216      -                }
 217      -        }
 218      -        return (currentlocale());
 219      -}
 220      -
 221      -static char *
 222      -currentlocale(void)
 223      -{
 224      -        int i;
 225      -        int composite = 0;
 226      -
 227      -        /* Look to see if any category is different */
 228      -        for (i = 1; i < NUM_CATS; ++i) {
 229      -                if (i == LC_ALL)
 230      -                        continue;
 231      -                if (strcmp(current_categories[0], current_categories[i])) {
 232      -                        composite = 1;
       88 +        /*
       89 +         * This next logic looks to see if we have ever used the same locale
       90 +         * settings before.  If so, we reuse it.  We avoid ever calling
       91 +         * freelocale() on a locale setting built up by setlocale, this
       92 +         * ensures that consumers (uselocale) will always be thread safe;
       93 +         * the actual locale data objects are never freed, and unique
       94 +         * locale objects are also never freed.  We reuse to avoid leaking
       95 +         * memory in applications that call setlocale repeatedly.
       96 +         */
       97 +        lmutex_lock(&setlocale_lock);
       98 +        for (srch = setlocale_list; srch != NULL; srch = srch->next) {
       99 +                if (strcmp(srch->locname, loc->locname) == 0) {
 233  100                          break;
 234  101                  }
 235  102          }
 236  103  
 237      -        if (composite) {
 238      -                /*
 239      -                 * Note ordering of these follows the numeric order,
 240      -                 * if the order is changed, then setlocale() will need
 241      -                 * to be changed as well.
 242      -                 */
 243      -                (void) snprintf(current_locale_string,
 244      -                    sizeof (current_locale_string),
 245      -                    "%s/%s/%s/%s/%s/%s",
 246      -                    current_categories[LC_CTYPE],
 247      -                    current_categories[LC_NUMERIC],
 248      -                    current_categories[LC_TIME],
 249      -                    current_categories[LC_COLLATE],
 250      -                    current_categories[LC_MONETARY],
 251      -                    current_categories[LC_MESSAGES]);
      104 +        if (srch == NULL) {
      105 +                /* this is a new locale, save it for reuse later */
      106 +                loc->next = setlocale_list;
      107 +                loc->on_list = 1;
      108 +                setlocale_list = loc;
 252  109          } else {
 253      -                (void) strlcpy(current_locale_string, current_categories[0],
 254      -                    sizeof (current_locale_string));
      110 +                /* we already had it, toss the new, and use what we found */
      111 +                freelocale(loc);
      112 +                loc = srch;
 255  113          }
 256      -        return (current_locale_string);
      114 +        ___global_locale = loc;
      115 +
      116 +        install_legacy(loc, mask);
      117 +        lmutex_unlock(&setlocale_lock);
      118 +
      119 +        return (current_locale(loc, category));
 257  120  }
 258  121  
 259  122  static char *
 260      -loadlocale(int category)
      123 +current_locale(locale_t loc, int cat)
 261  124  {
 262      -        char *new = new_categories[category];
 263      -        char *old = current_categories[category];
 264      -        int (*func)(const char *);
 265      -
 266      -        if ((new[0] == '.' &&
 267      -            (new[1] == '\0' || (new[1] == '.' && new[2] == '\0'))) ||
 268      -            strchr(new, '/') != NULL) {
 269      -                errno = EINVAL;
 270      -                return (NULL);
 271      -        }
 272      -
 273      -        switch (category) {
      125 +        switch (cat) {
 274  126          case LC_CTYPE:
 275      -                func = __wrap_setrunelocale;
 276      -                break;
 277  127          case LC_COLLATE:
 278      -                func = _collate_load_tables;
 279      -                break;
 280      -        case LC_TIME:
 281      -                func = __time_load_locale;
 282      -                break;
 283      -        case LC_NUMERIC:
 284      -                func = __numeric_load_locale;
 285      -                break;
 286      -        case LC_MONETARY:
 287      -                func = __monetary_load_locale;
 288      -                break;
 289  128          case LC_MESSAGES:
 290      -                func = __messages_load_locale;
 291      -                break;
      129 +        case LC_MONETARY:
      130 +        case LC_NUMERIC:
      131 +        case LC_TIME:
      132 +                return (loc->locdata[cat]->l_lname);
      133 +        case LC_ALL:
      134 +                return (loc->locname);
 292  135          default:
 293      -                errno = EINVAL;
 294  136                  return (NULL);
 295  137          }
 296      -
 297      -        if (strcmp(new, old) == 0)
 298      -                return (old);
 299      -
 300      -        if (func(new) != _LDP_ERROR) {
 301      -                (void) strcpy(old, new);
 302      -                return (old);
 303      -        }
 304      -
 305      -        return (NULL);
 306  138  }
 307  139  
 308      -static const char *
 309      -__get_locale_env(int category)
      140 +static void
      141 +install_legacy(locale_t loc, int mask)
 310  142  {
 311      -        const char *env;
      143 +        /*
      144 +         * Update the legacy fixed variables that may be baked into
      145 +         * legacy programs.  This is really unfortunate, but we can't
      146 +         * solve for them otherwise.  Note that such legacy programs
      147 +         * are only going to see the global locale settings, and cannot
      148 +         * benefit from uselocale().
      149 +         */
      150 +        if (mask & LC_NUMERIC_MASK) {
      151 +                struct lc_numeric *lnum;
      152 +                lnum = loc->locdata[LC_NUMERIC]->l_data[0];
      153 +                _numeric[0] = *lnum->decimal_point;
      154 +                _numeric[1] = *lnum->thousands_sep;
      155 +        }
 312  156  
 313      -        /* 1. check LC_ALL. */
 314      -        env = getenv(categories[LC_ALL]);
      157 +        if (mask & LC_CTYPE_MASK) {
      158 +                struct lc_ctype *lct;
      159 +                lct = loc->locdata[LC_CTYPE]->l_data[0];
      160 +                for (int i = 0; i < _CACHED_RUNES; i++) {
      161 +                        /* ctype can only encode the lower 8 bits. */
      162 +                        __ctype[i+1] = lct->lc_ctype_mask[i] & 0xff;
      163 +                        __ctype_mask[i] = lct->lc_ctype_mask[i];
      164 +                }
 315  165  
 316      -        /* 2. check LC_* */
 317      -        if (env == NULL || !*env)
 318      -                env = getenv(categories[category]);
      166 +                /* The bottom half is the toupper/lower array */
      167 +                for (int i = 0; i < _CACHED_RUNES; i++) {
      168 +                        int u, l;
      169 +                        __ctype[258 + i] = i;
      170 +                        u = lct->lc_trans_upper[i];
      171 +                        l = lct->lc_trans_lower[i];
      172 +                        if (u && u != i)
      173 +                                __ctype[258+i] = u;
      174 +                        if (l && l != i)
      175 +                                __ctype[258+i] = l;
 319  176  
 320      -        /* 3. check LANG */
 321      -        if (env == NULL || !*env)
 322      -                env = getenv("LANG");
      177 +                        /* Don't forget these annoyances either! */
      178 +                        __trans_upper[i] = u;
      179 +                        __trans_lower[i] = l;
      180 +                }
 323  181  
 324      -        /* 4. if none is set, fall to "C" */
 325      -        if (env == NULL || !*env)
 326      -                env = "C";
 327      -
 328      -        return (env);
      182 +                /* Maximum mblen, cswidth, weird legacy */
      183 +                __ctype[520] = lct->lc_max_mblen;
      184 +        }
 329  185  }
    
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX