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
12 13 * are met:
13 14 * 1. Redistributions of source code must retain the above copyright
14 15 * notice, this list of conditions and the following disclaimer.
15 16 * 2. Redistributions in binary form must reproduce the above copyright
16 17 * notice, this list of conditions and the following disclaimer in the
17 18 * documentation and/or other materials provided with the distribution.
18 19 * 4. Neither the name of the University nor the names of its contributors
19 20 * may be used to endorse or promote products derived from this software
20 21 * without specific prior written permission.
21 22 *
22 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 33 * SUCH DAMAGE.
33 34 */
↓ 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