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   46  #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() */
       47 +#include "lnumeric.h"   /* for struct lc_numeric */
       48 +#include "lctype.h"     /* for struct lc_ctype */
  50   49  #include "setlocale.h"
  51      -#include "ldpart.h"
  52      -#include "timelocal.h" /* for __time_load_locale() */
  53   50  #include "../i18n/_loc_path.h"
       51 +#include "localeimpl.h"
       52 +#include "../i18n/_locale.h"
  54   53  
  55   54  /*
  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   55   * Path to locale storage directory.  See ../i18n/_loc_path.h
  85   56   */
  86   57  char    *_PathLocale = _DFLT_LOC_PATH;
  87   58  
  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)];
       59 +static char     *current_locale(locale_t, int);
       60 +static void     install_legacy(locale_t, int);
  94   61  
  95      -static char     *currentlocale(void);
  96      -static char     *loadlocale(int);
  97      -static const char *__get_locale_env(int);
  98      -
  99   62  char *
 100      -setlocale(int category, const char *locale)
       63 +setlocale(int category, const char *locname)
 101   64  {
 102      -        int i, j, saverr;
 103      -        const char *env, *r;
       65 +        locale_t oldloc;
       66 +        locale_t newloc;
       67 +        int mask;
 104   68  
 105      -        if (category < 0 || category >= NUM_CATS) {
       69 +        if (category < 0 || category > LC_ALL) {
 106   70                  errno = EINVAL;
 107   71                  return (NULL);
 108   72          }
 109   73  
 110      -        if (locale == NULL)
 111      -                return (category != LC_ALL ?
 112      -                    current_categories[category] : currentlocale());
       74 +        if (locname == NULL)
       75 +                return (current_locale(___global_locale, category));
 113   76  
 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]);
       77 +        if ((oldloc = duplocale(___global_locale)) == NULL)
       78 +                return (NULL);
 119   79  
 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;
       80 +        mask = (category == LC_ALL ? LC_ALL_MASK : (1 << category));
 160   81  
 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      -                }
       82 +        newloc = newlocale(mask, locname, oldloc);
       83 +        if (newloc == NULL) {
       84 +                freelocale(oldloc);
       85 +                return (NULL);
 193   86          }
 194   87  
 195      -        if (category != LC_ALL)
 196      -                return (loadlocale(category));
       88 +        oldloc = ___global_locale;
       89 +        ___global_locale = newloc;
 197   90  
 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());
       91 +        install_legacy(newloc, mask);
       92 +
       93 +        freelocale(oldloc);
       94 +        return (current_locale(newloc, category));
 219   95  }
 220   96  
 221   97  static char *
 222      -currentlocale(void)
       98 +current_locale(locale_t loc, int cat)
 223   99  {
 224      -        int i;
 225  100          int composite = 0;
      101 +        static char buf[LC_ALL * (ENCODING_LEN + 1 + 1)];
 226  102  
      103 +        if (cat != LC_ALL) {
      104 +                return (loc->locdata[cat]->l_lname);
      105 +        }
      106 +
 227  107          /* 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])) {
      108 +        for (int i = 1; i < LC_ALL; ++i) {
      109 +                if (strcmp(loc->locdata[0]->l_lname,
      110 +                    loc->locdata[i]->l_lname) != 0) {
 232  111                          composite = 1;
 233  112                          break;
 234  113                  }
 235  114          }
 236  115  
 237  116          if (composite) {
 238  117                  /*
 239  118                   * Note ordering of these follows the numeric order,
 240  119                   * if the order is changed, then setlocale() will need
 241  120                   * to be changed as well.
 242  121                   */
 243      -                (void) snprintf(current_locale_string,
 244      -                    sizeof (current_locale_string),
      122 +                (void) snprintf(buf, sizeof (buf),
 245  123                      "%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]);
 252      -        } else {
 253      -                (void) strlcpy(current_locale_string, current_categories[0],
 254      -                    sizeof (current_locale_string));
      124 +                    loc->locdata[LC_CTYPE]->l_lname,
      125 +                    loc->locdata[LC_NUMERIC]->l_lname,
      126 +                    loc->locdata[LC_TIME]->l_lname,
      127 +                    loc->locdata[LC_COLLATE]->l_lname,
      128 +                    loc->locdata[LC_MONETARY]->l_lname,
      129 +                    loc->locdata[LC_MESSAGES]->l_lname);
      130 +                return (buf);
 255  131          }
 256      -        return (current_locale_string);
      132 +
      133 +        return (loc->locdata[0]->l_lname);
 257  134  }
 258  135  
 259      -static char *
 260      -loadlocale(int category)
      136 +static void
      137 +install_legacy(locale_t loc, int mask)
 261  138  {
 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);
      139 +        /*
      140 +         * Update the legacy fixed variables that may be baked into
      141 +         * legacy programs.  This is really unfortunate, but we can't
      142 +         * solve for them otherwise.  Note that such legacy programs
      143 +         * are only going to see the global locale settings, and cannot
      144 +         * benefit from uselocale().
      145 +         */
      146 +        if (mask & LC_NUMERIC_MASK) {
      147 +                struct lc_numeric *lnum;
      148 +                lnum = loc->locdata[LC_NUMERIC]->l_data[0];
      149 +                _numeric[0] = *lnum->decimal_point;
      150 +                _numeric[1] = *lnum->thousands_sep;
 271  151          }
 272  152  
 273      -        switch (category) {
 274      -        case LC_CTYPE:
 275      -                func = __wrap_setrunelocale;
 276      -                break;
 277      -        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      -        case LC_MESSAGES:
 290      -                func = __messages_load_locale;
 291      -                break;
 292      -        default:
 293      -                errno = EINVAL;
 294      -                return (NULL);
 295      -        }
      153 +        if (mask & LC_CTYPE_MASK) {
      154 +                struct lc_ctype *lct;
      155 +                lct = loc->locdata[LC_CTYPE]->l_data[0];
      156 +                for (int i = 0; i < _CACHED_RUNES; i++) {
      157 +                        /* ctype can only encode the lower 8 bits. */
      158 +                        __ctype[i+1] = lct->lc_ctype_mask[i] & 0xff;
      159 +                        __ctype_mask[i] = lct->lc_ctype_mask[i];
      160 +                }
 296  161  
 297      -        if (strcmp(new, old) == 0)
 298      -                return (old);
      162 +                /* The bottom half is the toupper/lower array */
      163 +                for (int i = 0; i < _CACHED_RUNES; i++) {
      164 +                        int u, l;
      165 +                        __ctype[258 + i] = i;
      166 +                        u = lct->lc_trans_upper[i];
      167 +                        l = lct->lc_trans_lower[i];
      168 +                        if (u && u != i)
      169 +                                __ctype[258+i] = u;
      170 +                        if (l && l != i)
      171 +                                __ctype[258+i] = l;
 299  172  
 300      -        if (func(new) != _LDP_ERROR) {
 301      -                (void) strcpy(old, new);
 302      -                return (old);
 303      -        }
      173 +                        /* Don't forget these annoyances either! */
      174 +                        __trans_upper[i] = u;
      175 +                        __trans_lower[i] = l;
      176 +                }
 304  177  
 305      -        return (NULL);
 306      -}
 307      -
 308      -static const char *
 309      -__get_locale_env(int category)
 310      -{
 311      -        const char *env;
 312      -
 313      -        /* 1. check LC_ALL. */
 314      -        env = getenv(categories[LC_ALL]);
 315      -
 316      -        /* 2. check LC_* */
 317      -        if (env == NULL || !*env)
 318      -                env = getenv(categories[category]);
 319      -
 320      -        /* 3. check LANG */
 321      -        if (env == NULL || !*env)
 322      -                env = getenv("LANG");
 323      -
 324      -        /* 4. if none is set, fall to "C" */
 325      -        if (env == NULL || !*env)
 326      -                env = "C";
 327      -
 328      -        return (env);
      178 +                /* Maximum mblen, cswidth, weird legacy */
      179 +                __ctype[520] = lct->lc_max_mblen;
      180 +        }
 329  181  }
    
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX