fix tolower and locales
[uclibc-ng.git] / libc / misc / locale / locale.c
blobd555f5da6a3ffcef4fbe253e290face039bbb86d
1 /* Copyright (C) 2002 Manuel Novoa III
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Library General Public
5 * License as published by the Free Software Foundation; either
6 * version 2 of the License, or (at your option) any later version.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Library General Public License for more details.
13 * You should have received a copy of the GNU Library General Public
14 * License along with this library; if not, see
15 * <http://www.gnu.org/licenses/>.
18 /* Nov. 1, 2002
19 * Reworked setlocale() return values and locale arg processing to
20 * be more like glibc. Applications expecting to be able to
21 * query locale settings should now work... at the cost of almost
22 * doubling the size of the setlocale object code.
23 * Fixed a bug in the internal fixed-size-string locale specifier code.
25 * Dec 20, 2002
26 * Added in collation support and updated stub nl_langinfo.
28 * Aug 1, 2003
29 * Added glibc-like extended locale stuff (newlocale, duplocale, etc).
31 * Aug 18, 2003
32 * Bug in duplocale... collation data wasn't copied.
33 * Bug in newlocale... translate 1<<LC_ALL to LC_ALL_MASK.
34 * Bug in _wchar_utf8sntowcs... fix cut-n-paste error.
36 * Aug 31, 2003
37 * Hack around bg_BG bug; grouping specified but no thousands separator.
38 * Also, disable the locale link_warnings for now, as they generate a
39 * lot of noise when using libstd++.
43 /* TODO:
44 * Implement the shared mmap code so non-mmu platforms can use this.
45 * Add some basic collate functionality similar to what the previous
46 * locale support had (8-bit codesets only).
49 #define __CTYPE_HAS_8_BIT_LOCALES 1
51 #include <string.h>
52 #include <stdlib.h>
53 #include <stddef.h>
54 #include <limits.h>
55 #include <stdint.h>
56 #include <assert.h>
57 #include <errno.h>
58 #include <ctype.h>
59 #include <stdio.h>
61 #undef __LOCALE_C_ONLY
62 #ifndef __UCLIBC_HAS_LOCALE__
63 #define __LOCALE_C_ONLY
64 #endif /* __UCLIBC_HAS_LOCALE__ */
67 #ifdef __LOCALE_C_ONLY
69 #include <locale.h>
71 #else /* __LOCALE_C_ONLY */
73 /* Need to include this before locale.h! */
74 #include <bits/uClibc_locale.h>
76 #undef CODESET_LIST
77 #define CODESET_LIST (__locale_mmap->codeset_list)
79 #ifdef __UCLIBC_HAS_XLOCALE__
80 #include <locale.h>
81 #else /* __UCLIBC_HAS_XLOCALE__ */
82 /* We need this internally... */
83 #define __UCLIBC_HAS_XLOCALE__ 1
84 #include <locale.h>
85 #undef __UCLIBC_HAS_XLOCALE__
86 #endif /* __UCLIBC_HAS_XLOCALE__ */
88 #include <wchar.h>
90 #define LOCALE_NAMES (__locale_mmap->locale_names5)
91 #define LOCALES (__locale_mmap->locales)
92 #define LOCALE_AT_MODIFIERS (__locale_mmap->locale_at_modifiers)
93 #define CATEGORY_NAMES (__locale_mmap->lc_names)
95 #define MAX_LOCALE_STR 256 /* TODO: Only sufficient for current case. */
96 #define MAX_LOCALE_CATEGORY_STR 32 /* TODO: Only sufficient for current case. */
97 /* Note: Best if MAX_LOCALE_CATEGORY_STR is a power of 2. */
99 extern int _locale_set_l(const unsigned char *p, __locale_t base) attribute_hidden;
100 extern void _locale_init_l(__locale_t base) attribute_hidden;
102 #endif /* __LOCALE_C_ONLY */
104 #undef LOCALE_STRING_SIZE
105 #define LOCALE_SELECTOR_SIZE (2 * __LC_ALL + 2)
107 #define C_LOCALE_SELECTOR "\x23\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80"
110 #include <langinfo.h>
111 #include <nl_types.h>
113 /**********************************************************************/
114 #ifdef L_setlocale
116 #ifdef __LOCALE_C_ONLY
118 static const char C_string[] = "C";
120 char *setlocale(int category, register const char *locale)
122 return ( (((unsigned int)(category)) <= LC_ALL)
123 && ( (!locale) /* Request for locale category string. */
124 || (!*locale) /* Implementation-defined default is C. */
125 || ((*locale == 'C') && !locale[1])
126 || (!strcmp(locale, "POSIX"))) )
127 ? (char *) C_string /* Always in C/POSIX locale. */
128 : NULL;
131 #else /* ---------------------------------------------- __LOCALE_C_ONLY */
133 #if !defined(__LOCALE_DATA_NUM_LOCALES) || (__LOCALE_DATA_NUM_LOCALES <= 1)
134 #error locales enabled, but not data other than for C locale!
135 #endif
137 static const char posix[] = "POSIX";
138 static const char utf8[] = "UTF-8";
140 /* Individual category strings start at hr_locale + category * MAX_LOCALE_CATEGORY.
141 * This holds for LC_ALL as well.
143 static char hr_locale[(MAX_LOCALE_CATEGORY_STR * LC_ALL) + MAX_LOCALE_STR];
146 static void update_hr_locale(const unsigned char *spec)
148 const unsigned char *loc;
149 const unsigned char *s;
150 char *n;
151 int i, category, done;
153 done = category = 0;
154 do {
155 s = spec + 1;
156 n = hr_locale + category * MAX_LOCALE_CATEGORY_STR;
158 if (category == LC_ALL) {
159 done = 1;
160 for (i = 0 ; i < LC_ALL-1 ; i += 2) {
161 if ((s[i] != s[i+2]) || (s[i+1] != s[i+3])) {
162 goto SKIP;
165 /* All categories the same, so simplify string by using a single
166 * category. */
167 category = LC_CTYPE;
170 SKIP:
171 i = (category == LC_ALL) ? 0 : category;
172 s += 2*i;
174 do {
175 if ((*s != 0xff) || (s[1] != 0xff)) {
176 loc = LOCALES
177 + __LOCALE_DATA_WIDTH_LOCALES * ((((int)(*s & 0x7f)) << 7)
178 + (s[1] & 0x7f));
179 if (category == LC_ALL) {
180 /* CATEGORY_NAMES is unsigned char* */
181 n = stpcpy(n, (char*) CATEGORY_NAMES + (int) CATEGORY_NAMES[i]);
182 *n++ = '=';
184 if (*loc == 0) {
185 *n++ = 'C';
186 *n = 0;
187 } else {
188 char at = 0;
189 memcpy(n, LOCALE_NAMES + 5*((*loc)-1), 5);
190 if (n[2] != '_') {
191 at = n[2];
192 n[2] = '_';
194 n += 5;
195 *n++ = '.';
196 if (loc[2] == 2) {
197 n = stpcpy(n, utf8);
198 } else if (loc[2] >= 3) {
199 n = stpcpy(n, (char*) CODESET_LIST + (int)(CODESET_LIST[loc[2] - 3]));
201 if (at) {
202 const char *q;
203 *n++ = '@';
204 q = (char*) LOCALE_AT_MODIFIERS;
205 do {
206 if (q[1] == at) {
207 n = stpcpy(n, q+2);
208 break;
210 q += 2 + *q;
211 } while (*q);
214 *n++ = ';';
216 s += 2;
217 } while (++i < category);
218 *--n = 0; /* Remove trailing ';' and nul-terminate. */
220 ++category;
221 } while (!done);
224 char *setlocale(int category, const char *locale)
226 if (((unsigned int)(category)) > LC_ALL) {
227 #if 0
228 __set_errno(EINVAL); /* glibc sets errno -- SUSv3 doesn't say. */
229 #endif
230 return NULL; /* Illegal/unsupported category. */
233 if (locale != NULL) { /* Not just a query... */
234 if (!newlocale((1 << category), locale, __global_locale)) {
235 return NULL; /* Failed! */
237 update_hr_locale(__global_locale->cur_locale);
240 /* Either a query or a successful set, so return current locale string. */
241 return hr_locale + (category * MAX_LOCALE_CATEGORY_STR);
244 #endif /* __LOCALE_C_ONLY */
246 #endif
247 /**********************************************************************/
248 #ifdef L_localeconv
250 /* Note: We assume here that the compiler does the sane thing regarding
251 * placement of the fields in the struct. If necessary, we could ensure
252 * this usings an array of offsets but at some size cost. */
255 #ifdef __LOCALE_C_ONLY
257 static struct lconv the_lconv;
259 static const char decpt[] = ".";
261 struct lconv *localeconv(void)
263 register char *p = (char *)(&the_lconv);
265 *((char **)p) = (char *) decpt;
266 do {
267 p += sizeof(char **);
268 *((char **)p) = (char *) (decpt+1);
269 } while (p < (char *) &the_lconv.negative_sign);
271 p = (&the_lconv.int_frac_digits);
272 do {
273 *p = CHAR_MAX;
274 ++p;
275 } while (p <= &the_lconv.int_n_sign_posn);
277 return &the_lconv;
280 #else /* __LOCALE_C_ONLY */
282 static struct lconv the_lconv;
284 struct lconv *localeconv(void)
286 register char *p = (char *) &the_lconv;
287 register char **q = (char **) &(__UCLIBC_CURLOCALE->decimal_point);
289 do {
290 *((char **)p) = *q;
291 p += sizeof(char **);
292 ++q;
293 } while (p < &the_lconv.int_frac_digits);
295 do {
296 *p = **q;
297 ++p;
298 ++q;
299 } while (p <= &the_lconv.int_n_sign_posn);
301 return &the_lconv;
304 #endif /* __LOCALE_C_ONLY */
306 libc_hidden_def(localeconv)
308 #endif
309 /**********************************************************************/
310 #if defined(L__locale_init) && !defined(__LOCALE_C_ONLY)
312 struct __uclibc_locale_struct __global_locale_data;
314 __locale_t __global_locale = &__global_locale_data;
316 #ifdef __UCLIBC_HAS_XLOCALE__
317 __locale_t __curlocale_var = &__global_locale_data;
318 #endif
320 /*----------------------------------------------------------------------*/
321 static const char utf8[] = "UTF-8";
322 static const char ascii[] = "ASCII";
324 typedef struct {
325 uint16_t num_base;
326 uint16_t num_der;
327 uint16_t MAX_WEIGHTS;
328 uint16_t num_index2weight;
329 #define num_index2ruleidx num_index2weight
330 uint16_t num_weightstr;
331 uint16_t num_multistart;
332 uint16_t num_override;
333 uint16_t num_ruletable;
334 } coldata_header_t;
336 typedef struct {
337 uint16_t num_weights;
338 uint16_t num_starters;
339 uint16_t ii_shift;
340 uint16_t ti_shift;
341 uint16_t ii_len;
342 uint16_t ti_len;
343 uint16_t max_weight;
344 uint16_t num_col_base;
345 uint16_t max_col_index;
346 uint16_t undefined_idx;
347 uint16_t range_low;
348 uint16_t range_count;
349 uint16_t range_base_weight;
350 uint16_t range_rule_offset;
352 uint16_t index2weight_offset;
353 uint16_t index2ruleidx_offset;
354 uint16_t multistart_offset;
355 uint16_t wcs2colidt_offset_low;
356 uint16_t wcs2colidt_offset_hi;
357 } coldata_base_t;
359 typedef struct {
360 uint16_t base_idx;
361 uint16_t undefined_idx;
362 uint16_t overrides_offset;
363 uint16_t multistart_offset;
364 } coldata_der_t;
366 static int init_cur_collate(int der_num, __collate_t *cur_collate)
368 const uint16_t *__locale_collate_tbl = __locale_mmap->collate_data;
369 coldata_header_t *cdh;
370 coldata_base_t *cdb;
371 coldata_der_t *cdd;
372 const uint16_t *p;
373 size_t n;
374 uint16_t i, w;
376 #if 0
377 assert(sizeof(coldata_base_t) == 19*2);
378 assert(sizeof(coldata_der_t) == 4*2);
379 assert(sizeof(coldata_header_t) == 8*2);
380 #endif
382 if (!der_num) { /* C locale... special */
383 cur_collate->num_weights = 0;
384 return 1;
387 --der_num;
389 cdh = (coldata_header_t *) __locale_collate_tbl;
391 #if 0
392 if (der_num >= cdh->num_der) {
393 return 0;
395 #else
396 assert((der_num < cdh->num_der));
397 #endif
399 cdd = (coldata_der_t *)(__locale_collate_tbl
400 + (sizeof(coldata_header_t)
401 + cdh->num_base * sizeof(coldata_base_t)
402 + der_num * sizeof(coldata_der_t)
403 )/2 );
405 cdb = (coldata_base_t *)(__locale_collate_tbl
406 + (sizeof(coldata_header_t)
407 + cdd->base_idx * sizeof(coldata_base_t)
408 )/2 );
410 memcpy(cur_collate, cdb, offsetof(coldata_base_t,index2weight_offset));
411 cur_collate->undefined_idx = cdd->undefined_idx;
413 cur_collate->ti_mask = (1 << cur_collate->ti_shift)-1;
414 cur_collate->ii_mask = (1 << cur_collate->ii_shift)-1;
416 /* fflush(stdout); */
417 /* fprintf(stderr,"base=%d num_col_base: %d %d\n", cdd->base_idx ,cur_collate->num_col_base, cdb->num_col_base); */
419 n = (sizeof(coldata_header_t) + cdh->num_base * sizeof(coldata_base_t)
420 + cdh->num_der * sizeof(coldata_der_t))/2;
422 /* fprintf(stderr,"n = %d\n", n); */
423 cur_collate->index2weight_tbl = __locale_collate_tbl + n + cdb->index2weight_offset;
424 /* fprintf(stderr,"i2w = %d\n", n + cdb->index2weight_offset); */
425 n += cdh->num_index2weight;
426 cur_collate->index2ruleidx_tbl = __locale_collate_tbl + n + cdb->index2ruleidx_offset;
427 /* fprintf(stderr,"i2r = %d\n", n + cdb->index2ruleidx_offset); */
428 n += cdh->num_index2ruleidx;
429 cur_collate->multistart_tbl = __locale_collate_tbl + n + cdd->multistart_offset;
430 /* fprintf(stderr,"mts = %d\n", n + cdb->multistart_offset); */
431 n += cdh->num_multistart;
432 cur_collate->overrides_tbl = __locale_collate_tbl + n + cdd->overrides_offset;
433 /* fprintf(stderr,"ovr = %d\n", n + cdd->overrides_offset); */
434 n += cdh->num_override;
435 cur_collate->ruletable = __locale_collate_tbl + n;
436 /* fprintf(stderr, "rtb = %d\n", n); */
437 n += cdh->num_ruletable;
438 cur_collate->weightstr = __locale_collate_tbl + n;
439 /* fprintf(stderr,"wts = %d\n", n); */
440 n += cdh->num_weightstr;
441 cur_collate->wcs2colidt_tbl = __locale_collate_tbl + n
442 + (((unsigned long)(cdb->wcs2colidt_offset_hi)) << 16)
443 + cdb->wcs2colidt_offset_low;
444 /* fprintf(stderr,"wcs = %lu\n", n + (((unsigned long)(cdb->wcs2colidt_offset_hi)) << 16) */
445 /* + cdb->wcs2colidt_offset_low); */
447 cur_collate->MAX_WEIGHTS = cdh->MAX_WEIGHTS;
449 cur_collate->index2weight = calloc(2*cur_collate->max_col_index+2,
450 sizeof(uint16_t));
451 if (!cur_collate->index2weight) {
452 return 0;
454 cur_collate->index2ruleidx = cur_collate->index2weight
455 + cur_collate->max_col_index + 1;
457 memcpy(cur_collate->index2weight, cur_collate->index2weight_tbl,
458 cur_collate->num_col_base * sizeof(uint16_t));
459 memcpy(cur_collate->index2ruleidx, cur_collate->index2ruleidx_tbl,
460 cur_collate->num_col_base * sizeof(uint16_t));
462 /* now do the overrides */
463 p = cur_collate->overrides_tbl;
464 while (*p > 1) {
465 /* fprintf(stderr, "processing override -- count = %d\n", *p); */
466 n = *p++;
467 w = *p++;
468 do {
469 i = *p++;
470 /* fprintf(stderr, " i=%d (%#x) w=%d *p=%d\n", i, i, w, *p); */
471 cur_collate->index2weight[i-1] = w++;
472 cur_collate->index2ruleidx[i-1] = *p++;
473 } while (--n);
475 assert(*p == 1);
476 while (*++p) {
477 i = *p;
478 /* fprintf(stderr, " i=%d (%#x) w=%d *p=%d\n", i, i, p[1], p[2]); */
479 cur_collate->index2weight[i-1] = *++p;
480 cur_collate->index2ruleidx[i-1] = *++p;
484 for (i=0 ; i < cur_collate->multistart_tbl[0] ; i++) {
485 p = cur_collate->multistart_tbl;
486 /* fprintf(stderr, "%2d of %2d: %d ", i, cur_collate->multistart_tbl[0], p[i]); */
487 p += p[i];
489 do {
490 n = *p++;
491 do {
492 if (!*p) { /* found it */
493 /* fprintf(stderr, "found: n=%d (%#lx) |%.*ls|\n", n, (int) *cs->s, n, cs->s); */
494 /* fprintf(stderr, ": %d - single\n", n); */
495 goto FOUND;
497 /* the lookup check here is safe since we're assured that *p is a valid colidex */
498 /* fprintf(stderr, "lookup(%lc)==%d *p==%d\n", cs->s[n], lookup(cs->s[n]), (int) *p); */
499 /* fprintf(stderr, ": %d - ", n); */
500 do {
501 /* fprintf(stderr, "%d|", *p); */
502 } while (*p++);
503 break;
504 } while (1);
505 } while (1);
506 FOUND:
507 continue;
510 return 1;
513 int attribute_hidden _locale_set_l(const unsigned char *p, __locale_t base)
515 const char **x;
516 unsigned char *s = base->cur_locale + 1;
517 const size_t *stp;
518 const unsigned char *r;
519 const uint16_t *io;
520 const uint16_t *ii;
521 const unsigned char *d;
522 int row; /* locale row */
523 int crow; /* category row */
524 int len;
525 int c;
526 int i = 0;
527 __collate_t newcol;
529 ++p;
531 newcol.index2weight = NULL;
532 if ((p[2*LC_COLLATE] != s[2*LC_COLLATE])
533 || (p[2*LC_COLLATE + 1] != s[2*LC_COLLATE + 1])
535 row = (((int)(*p & 0x7f)) << 7) + (p[1] & 0x7f);
536 assert(row < __LOCALE_DATA_NUM_LOCALES);
537 if (!init_cur_collate(__locale_mmap->locales[ __LOCALE_DATA_WIDTH_LOCALES
538 * row + 3 + LC_COLLATE ],
539 &newcol)
541 return 0; /* calloc failed. */
543 free(base->collate.index2weight);
544 memcpy(&base->collate, &newcol, sizeof(__collate_t));
547 do {
548 if ((*p != *s) || (p[1] != s[1])) {
549 row = (((int)(*p & 0x7f)) << 7) + (p[1] & 0x7f);
550 assert(row < __LOCALE_DATA_NUM_LOCALES);
552 *s = *p;
553 s[1] = p[1];
555 if ((i != LC_COLLATE)
556 && ((len = __locale_mmap->lc_common_item_offsets_LEN[i]) != 0)
558 crow = __locale_mmap->locales[ __LOCALE_DATA_WIDTH_LOCALES * row
559 + 3 + i ]
560 * len;
562 x = (const char **)(((char *) base)
563 + base->category_offsets[i]);
565 stp = __locale_mmap->lc_common_tbl_offsets + 4*i;
566 r = (const unsigned char *)( ((char *)__locale_mmap) + *stp );
567 io = (const uint16_t *)( ((char *)__locale_mmap) + *++stp );
568 ii = (const uint16_t *)( ((char *)__locale_mmap) + *++stp );
569 d = (const unsigned char *)( ((char *)__locale_mmap) + *++stp );
570 for (c = 0; c < len; c++) {
571 x[c] = (char*)(d + ii[r[crow + c] + io[c]]);
574 if (i == LC_CTYPE) {
575 c = __locale_mmap->locales[ __LOCALE_DATA_WIDTH_LOCALES * row
576 + 2 ]; /* codeset */
577 if (c <= 2) {
578 if (c == 2) {
579 base->codeset = utf8;
580 base->encoding = __ctype_encoding_utf8;
581 /* TODO - fix for bcc */
582 base->mb_cur_max = 6;
583 } else {
584 assert(c == 1);
585 base->codeset = ascii;
586 base->encoding = __ctype_encoding_7_bit;
587 base->mb_cur_max = 1;
589 } else {
590 const __codeset_8_bit_t *c8b;
591 r = CODESET_LIST;
592 c -= 3;
593 base->codeset = (char *) (r + r[c]);
594 base->encoding = __ctype_encoding_8_bit;
595 /* TODO - update when translit implemented! */
596 base->mb_cur_max = 1;
597 c8b = __locale_mmap->codeset_8_bit + c;
598 #ifdef __CTYPE_HAS_8_BIT_LOCALES
599 base->idx8ctype = c8b->idx8ctype;
600 base->idx8uplow = c8b->idx8uplow;
601 #ifdef __UCLIBC_HAS_WCHAR__
602 base->idx8c2wc = c8b->idx8c2wc;
603 base->idx8wc2c = c8b->idx8wc2c;
604 /* translit */
605 #endif /* __UCLIBC_HAS_WCHAR__ */
607 /* What follows is fairly bloated, but it is just a hack
608 * to get the 8-bit codeset ctype stuff functioning.
609 * All of this will be replaced in the next generation
610 * of locale support anyway... */
612 memcpy(base->__ctype_b_data,
613 __C_ctype_b - __UCLIBC_CTYPE_B_TBL_OFFSET,
614 (256 + __UCLIBC_CTYPE_B_TBL_OFFSET)
615 * sizeof(__ctype_mask_t));
616 memcpy(base->__ctype_tolower_data,
617 __C_ctype_tolower - __UCLIBC_CTYPE_TO_TBL_OFFSET,
618 (256 + __UCLIBC_CTYPE_TO_TBL_OFFSET)
619 * sizeof(__ctype_touplow_t));
620 memcpy(base->__ctype_toupper_data,
621 __C_ctype_toupper - __UCLIBC_CTYPE_TO_TBL_OFFSET,
622 (256 + __UCLIBC_CTYPE_TO_TBL_OFFSET)
623 * sizeof(__ctype_touplow_t));
625 #define Cctype_TBL_MASK ((1 << __LOCALE_DATA_Cctype_IDX_SHIFT) - 1)
626 #define Cctype_IDX_OFFSET (128 >> __LOCALE_DATA_Cctype_IDX_SHIFT)
629 int u;
630 __ctype_mask_t m;
632 for (u=0 ; u < 128 ; u++) {
633 #ifdef __LOCALE_DATA_Cctype_PACKED
634 c = base->tbl8ctype
635 [ ((int)(c8b->idx8ctype
636 [(u >> __LOCALE_DATA_Cctype_IDX_SHIFT) ])
637 << (__LOCALE_DATA_Cctype_IDX_SHIFT - 1))
638 + ((u & Cctype_TBL_MASK) >> 1)];
639 c = (u & 1) ? (c >> 4) : (c & 0xf);
640 #else
641 c = base->tbl8ctype
642 [ ((int)(c8b->idx8ctype
643 [(u >> __LOCALE_DATA_Cctype_IDX_SHIFT) ])
644 << __LOCALE_DATA_Cctype_IDX_SHIFT)
645 + (u & Cctype_TBL_MASK) ];
646 #endif
648 m = base->code2flag[c];
650 base->__ctype_b_data
651 [128 + __UCLIBC_CTYPE_B_TBL_OFFSET + u]
652 = m;
654 #ifdef __UCLIBC_HAS_CTYPE_SIGNED__
655 if (((signed char)(128 + u)) != -1) {
656 base->__ctype_b_data[__UCLIBC_CTYPE_B_TBL_OFFSET
657 + ((signed char)(128 + u))]
658 = m;
660 #endif
662 base->__ctype_tolower_data
663 [128 + __UCLIBC_CTYPE_TO_TBL_OFFSET + u]
664 = 128 + u;
665 base->__ctype_toupper_data
666 [128 + __UCLIBC_CTYPE_TO_TBL_OFFSET + u]
667 = 128 + u;
669 if (m & (_ISlower|_ISupper)) {
670 c = base->tbl8uplow
671 [ ((int)(c8b->idx8uplow
672 [u >> __LOCALE_DATA_Cuplow_IDX_SHIFT])
673 << __LOCALE_DATA_Cuplow_IDX_SHIFT)
674 + ((128 + u)
675 & ((1 << __LOCALE_DATA_Cuplow_IDX_SHIFT)
676 - 1)) ];
677 if (m & _ISlower) {
678 base->__ctype_toupper_data
679 [128 + __UCLIBC_CTYPE_TO_TBL_OFFSET + u]
680 = (unsigned char)(128 + u + c);
681 #ifdef __UCLIBC_HAS_CTYPE_SIGNED__
682 if (((signed char)(128 + u)) != -1) {
683 base->__ctype_toupper_data
684 [__UCLIBC_CTYPE_TO_TBL_OFFSET
685 + ((signed char)(128 + u))]
686 = (unsigned char)(128 + u + c);
688 #endif
689 } else {
690 base->__ctype_tolower_data
691 [128 + __UCLIBC_CTYPE_TO_TBL_OFFSET + u]
692 = (unsigned char)(128 + u - c);
693 #ifdef __UCLIBC_HAS_CTYPE_SIGNED__
694 if (((signed char)(128 + u)) != -1) {
695 base->__ctype_tolower_data
696 [__UCLIBC_CTYPE_TO_TBL_OFFSET
697 + ((signed char)(128 + u))]
698 = (unsigned char)(128 + u - c);
700 #endif
706 #ifdef __UCLIBC_HAS_XLOCALE__
707 base->__ctype_b = base->__ctype_b_data
708 + __UCLIBC_CTYPE_B_TBL_OFFSET;
709 base->__ctype_tolower = base->__ctype_tolower_data
710 + __UCLIBC_CTYPE_TO_TBL_OFFSET;
711 base->__ctype_toupper = base->__ctype_toupper_data
712 + __UCLIBC_CTYPE_TO_TBL_OFFSET;
713 #else /* __UCLIBC_HAS_XLOCALE__ */
714 __ctype_b = base->__ctype_b_data
715 + __UCLIBC_CTYPE_B_TBL_OFFSET;
716 __ctype_tolower = base->__ctype_tolower_data
717 + __UCLIBC_CTYPE_TO_TBL_OFFSET;
718 __ctype_toupper = base->__ctype_toupper_data
719 + __UCLIBC_CTYPE_TO_TBL_OFFSET;
720 #endif /* __UCLIBC_HAS_XLOCALE__ */
722 #endif /* __CTYPE_HAS_8_BIT_LOCALES */
724 d = base->outdigit_length;
725 x = &base->outdigit0_mb;
726 for (c = 0 ; c < 10 ; c++) {
727 ((unsigned char *)d)[c] = strlen(x[c]);
728 assert(d[c] > 0);
730 } else if (i == LC_NUMERIC) {
731 assert(LC_NUMERIC > LC_CTYPE); /* Need ctype initialized. */
733 base->decimal_point_len
734 = __locale_mbrtowc_l(&base->decimal_point_wc,
735 base->decimal_point, base);
736 assert(base->decimal_point_len > 0);
737 assert(base->decimal_point[base->decimal_point_len] == 0);
739 if (*base->grouping) {
740 base->thousands_sep_len
741 = __locale_mbrtowc_l(&base->thousands_sep_wc,
742 base->thousands_sep, base);
743 #if 1
744 assert(base->thousands_sep_len >= 0);
745 if (base->thousands_sep_len == 0) {
746 base->grouping = base->thousands_sep; /* empty string */
748 assert(base->thousands_sep[base->thousands_sep_len] == 0);
749 #else
750 assert(base->thousands_sep_len > 0);
751 assert(base->thousands_sep[base->thousands_sep_len] == 0);
752 #endif
755 /* } else if (i == LC_COLLATE) { */
756 /* init_cur_collate(__locale_mmap->locales[ __LOCALE_DATA_WIDTH_LOCALES */
757 /* * row + 3 + i ], */
758 /* &base->collate); */
761 ++i;
762 p += 2;
763 s += 2;
764 } while (i < LC_ALL);
766 return 1;
769 static const uint16_t __code2flag[16] = {
770 0, /* unclassified = 0 */
771 _ISprint|_ISgraph|_ISalnum|_ISalpha, /* alpha_nonupper_nonlower */
772 _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, /* alpha_lower */
773 _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower|_ISupper, /* alpha_upper_lower */
774 _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, /* alpha_upper */
775 _ISprint|_ISgraph|_ISalnum|_ISdigit, /* digit */
776 _ISprint|_ISgraph|_ISpunct, /* punct */
777 _ISprint|_ISgraph, /* graph */
778 _ISprint|_ISspace, /* print_space_nonblank */
779 _ISprint|_ISspace|_ISblank, /* print_space_blank */
780 _ISspace, /* space_nonblank_noncntrl */
781 _ISspace|_ISblank, /* space_blank_noncntrl */
782 _IScntrl|_ISspace, /* cntrl_space_nonblank */
783 _IScntrl|_ISspace|_ISblank, /* cntrl_space_blank */
784 _IScntrl /* cntrl_nonspace */
787 void attribute_hidden _locale_init_l(__locale_t base)
789 memset(base->cur_locale, 0, LOCALE_SELECTOR_SIZE);
790 base->cur_locale[0] = '#';
792 memcpy(base->category_item_count,
793 __locale_mmap->lc_common_item_offsets_LEN,
794 LC_ALL);
796 ++base->category_item_count[0]; /* Increment for codeset entry. */
797 base->category_offsets[0] = offsetof(struct __uclibc_locale_struct, outdigit0_mb);
798 base->category_offsets[1] = offsetof(struct __uclibc_locale_struct, decimal_point);
799 base->category_offsets[2] = offsetof(struct __uclibc_locale_struct, int_curr_symbol);
800 base->category_offsets[3] = offsetof(struct __uclibc_locale_struct, abday_1);
801 /* base->category_offsets[4] = offsetof(struct __uclibc_locale_struct, collate???); */
802 base->category_offsets[5] = offsetof(struct __uclibc_locale_struct, yesexpr);
804 #ifdef __CTYPE_HAS_8_BIT_LOCALES
805 base->tbl8ctype
806 = (const unsigned char *) &__locale_mmap->tbl8ctype;
807 base->tbl8uplow
808 = (const unsigned char *) &__locale_mmap->tbl8uplow;
809 #ifdef __UCLIBC_HAS_WCHAR__
810 base->tbl8c2wc
811 = (const uint16_t *) &__locale_mmap->tbl8c2wc;
812 base->tbl8wc2c
813 = (const unsigned char *) &__locale_mmap->tbl8wc2c;
814 /* translit */
815 #endif /* __UCLIBC_HAS_WCHAR__ */
816 #endif /* __CTYPE_HAS_8_BIT_LOCALES */
817 #ifdef __UCLIBC_HAS_WCHAR__
818 base->tblwctype
819 = (const unsigned char *) &__locale_mmap->tblwctype;
820 base->tblwuplow
821 = (const unsigned char *) &__locale_mmap->tblwuplow;
822 base->tblwuplow_diff
823 = (const int32_t *) &__locale_mmap->tblwuplow_diff;
824 /* base->tblwcomb */
825 /* = (const unsigned char *) &__locale_mmap->tblwcomb; */
826 /* width?? */
827 #endif /* __UCLIBC_HAS_WCHAR__ */
829 /* Initially, set things up to use the global C ctype tables.
830 * This is correct for C (ASCII) and UTF-8 based locales (except tr_TR). */
831 #ifdef __UCLIBC_HAS_XLOCALE__
832 base->__ctype_b = __C_ctype_b;
833 base->__ctype_tolower = __C_ctype_tolower;
834 base->__ctype_toupper = __C_ctype_toupper;
835 #else /* __UCLIBC_HAS_XLOCALE__ */
836 __ctype_b = __C_ctype_b;
837 __ctype_tolower = __C_ctype_tolower;
838 __ctype_toupper = __C_ctype_toupper;
839 #endif /* __UCLIBC_HAS_XLOCALE__ */
841 base->code2flag = __code2flag;
843 _locale_set_l((unsigned char*) C_LOCALE_SELECTOR, base);
846 void _locale_init(void)
848 /* TODO: mmap the locale file */
850 /* TODO - ??? */
851 _locale_init_l(__global_locale);
854 #endif
855 /**********************************************************************/
856 #if defined(L_nl_langinfo) || defined(L_nl_langinfo_l)
858 #ifdef __LOCALE_C_ONLY
860 /* We need to index 320 bytes of data, so you might initially think we
861 * need to store the offsets in shorts. But since the offset of the
862 * 64th item is 182, we'll store "offset - 2*64" for all items >= 64
863 * and always calculate the data offset as "offset[i] + 2*(i & 64)".
864 * This allows us to pack the data offsets in an unsigned char while
865 * also avoiding an "if".
867 * Note: Category order is assumed to be:
868 * ctype, numeric, monetary, time, collate, messages, all
871 #define C_LC_ALL 6
873 /* Combine the data to avoid size penalty for seperate char arrays when
874 * compiler aligns objects. The original code is left in as documentation. */
875 #define cat_start nl_data
876 #define C_locale_data (nl_data + C_LC_ALL + 1 + 90)
878 static const unsigned char nl_data[C_LC_ALL + 1 + 90 + 320] = {
879 /* static const char cat_start[LC_ALL + 1] = { */
880 '\x00', '\x0b', '\x0e', '\x24', '\x56', '\x56', '\x5a',
881 /* }; */
882 /* static const char item_offset[90] = { */
883 '\x00', '\x02', '\x04', '\x06', '\x08', '\x0a', '\x0c', '\x0e',
884 '\x10', '\x12', '\x14', '\x1a', '\x1b', '\x1b', '\x1b', '\x1b',
885 '\x1b', '\x1b', '\x1b', '\x1b', '\x1b', '\x1c', '\x1c', '\x1c',
886 '\x1c', '\x1c', '\x1c', '\x1c', '\x1c', '\x1c', '\x1c', '\x1c',
887 '\x1c', '\x1c', '\x1c', '\x1e', '\x20', '\x24', '\x28', '\x2c',
888 '\x30', '\x34', '\x38', '\x3c', '\x43', '\x4a', '\x52', '\x5c',
889 '\x65', '\x6c', '\x75', '\x79', '\x7d', '\x81', '\x85', '\x89',
890 '\x8d', '\x91', '\x95', '\x99', '\x9d', '\xa1', '\xa5', '\xad',
891 '\x36', '\x3c', '\x42', '\x46', '\x4b', '\x50', '\x57', '\x61',
892 '\x69', '\x72', '\x7b', '\x7e', '\x81', '\x96', '\x9f', '\xa8',
893 '\xb3', '\xb3', '\xb3', '\xb3', '\xb3', '\xb3', '\xb4', '\xba',
894 '\xbf', '\xbf',
895 /* }; */
896 /* static const char C_locale_data[320] = { */
897 '0', '\x00', '1', '\x00', '2', '\x00', '3', '\x00',
898 '4', '\x00', '5', '\x00', '6', '\x00', '7', '\x00',
899 '8', '\x00', '9', '\x00', 'A', 'S', 'C', 'I',
900 'I', '\x00', '.', '\x00', '\x7f', '\x00', '-', '\x00',
901 'S', 'u', 'n', '\x00', 'M', 'o', 'n', '\x00',
902 'T', 'u', 'e', '\x00', 'W', 'e', 'd', '\x00',
903 'T', 'h', 'u', '\x00', 'F', 'r', 'i', '\x00',
904 'S', 'a', 't', '\x00', 'S', 'u', 'n', 'd',
905 'a', 'y', '\x00', 'M', 'o', 'n', 'd', 'a',
906 'y', '\x00', 'T', 'u', 'e', 's', 'd', 'a',
907 'y', '\x00', 'W', 'e', 'd', 'n', 'e', 's',
908 'd', 'a', 'y', '\x00', 'T', 'h', 'u', 'r',
909 's', 'd', 'a', 'y', '\x00', 'F', 'r', 'i',
910 'd', 'a', 'y', '\x00', 'S', 'a', 't', 'u',
911 'r', 'd', 'a', 'y', '\x00', 'J', 'a', 'n',
912 '\x00', 'F', 'e', 'b', '\x00', 'M', 'a', 'r',
913 '\x00', 'A', 'p', 'r', '\x00', 'M', 'a', 'y',
914 '\x00', 'J', 'u', 'n', '\x00', 'J', 'u', 'l',
915 '\x00', 'A', 'u', 'g', '\x00', 'S', 'e', 'p',
916 '\x00', 'O', 'c', 't', '\x00', 'N', 'o', 'v',
917 '\x00', 'D', 'e', 'c', '\x00', 'J', 'a', 'n',
918 'u', 'a', 'r', 'y', '\x00', 'F', 'e', 'b',
919 'r', 'u', 'a', 'r', 'y', '\x00', 'M', 'a',
920 'r', 'c', 'h', '\x00', 'A', 'p', 'r', 'i',
921 'l', '\x00', 'M', 'a', 'y', '\x00', 'J', 'u',
922 'n', 'e', '\x00', 'J', 'u', 'l', 'y', '\x00',
923 'A', 'u', 'g', 'u', 's', 't', '\x00', 'S',
924 'e', 'p', 't', 'e', 'm', 'b', 'e', 'r',
925 '\x00', 'O', 'c', 't', 'o', 'b', 'e', 'r',
926 '\x00', 'N', 'o', 'v', 'e', 'm', 'b', 'e',
927 'r', '\x00', 'D', 'e', 'c', 'e', 'm', 'b',
928 'e', 'r', '\x00', 'A', 'M', '\x00', 'P', 'M',
929 '\x00', '%', 'a', ' ', '%', 'b', ' ', '%',
930 'e', ' ', '%', 'H', ':', '%', 'M', ':',
931 '%', 'S', ' ', '%', 'Y', '\x00', '%', 'm',
932 '/', '%', 'd', '/', '%', 'y', '\x00', '%',
933 'H', ':', '%', 'M', ':', '%', 'S', '\x00',
934 '%', 'I', ':', '%', 'M', ':', '%', 'S',
935 ' ', '%', 'p', '\x00', '^', '[', 'y', 'Y',
936 ']', '\x00', '^', '[', 'n', 'N', ']', '\x00',
939 char *nl_langinfo(nl_item item)
941 unsigned int c;
942 unsigned int i;
944 if ((c = _NL_ITEM_CATEGORY(item)) < C_LC_ALL) {
945 if ((i = cat_start[c] + _NL_ITEM_INDEX(item)) < cat_start[c+1]) {
946 /* return (char *) C_locale_data + item_offset[i] + (i & 64); */
947 return (char *) C_locale_data + nl_data[C_LC_ALL+1+i] + 2*(i & 64);
950 return (char *) cat_start; /* Conveniently, this is the empty string. */
952 libc_hidden_def(nl_langinfo)
954 #else /* __LOCALE_C_ONLY */
956 #if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE)
960 char *nl_langinfo(nl_item item)
962 return nl_langinfo_l(item, __UCLIBC_CURLOCALE);
964 libc_hidden_def(nl_langinfo)
966 #else /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
968 libc_hidden_proto(__XL_NPP(nl_langinfo))
970 static const char empty[] = "";
972 char *__XL_NPP(nl_langinfo)(nl_item item __LOCALE_PARAM )
974 unsigned int c = _NL_ITEM_CATEGORY(item);
975 unsigned int i = _NL_ITEM_INDEX(item);
977 if ((c < LC_ALL) && (i < __LOCALE_PTR->category_item_count[c])) {
978 return ((char **)(((char *) __LOCALE_PTR)
979 + __LOCALE_PTR->category_offsets[c]))[i];
982 return (char *) empty;
984 libc_hidden_def(__XL_NPP(nl_langinfo))
986 #endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
988 #endif /* __LOCALE_C_ONLY */
990 #endif
991 /**********************************************************************/
992 #ifdef L_newlocale
994 static const char posix[] = "POSIX";
995 static const char utf8[] = "UTF-8";
997 static int find_locale(int category_mask, const char *p,
998 unsigned char *new_locale)
1000 int i;
1001 const unsigned char *s;
1002 uint16_t n;
1003 unsigned char lang_cult, codeset;
1005 #if defined(__LOCALE_DATA_AT_MODIFIERS_LENGTH) && 1
1006 /* Support standard locale handling for @-modifiers. */
1008 char buf[18]; /* TODO: 7+{max codeset name length} */
1009 const char *q;
1011 if ((q = strchr(p,'@')) != NULL) {
1012 if ((((size_t)((q-p)-5)) > (sizeof(buf) - 5)) || (p[2] != '_')) {
1013 return 0;
1015 /* locale name at least 5 chars long and 3rd char is '_' */
1016 s = LOCALE_AT_MODIFIERS;
1017 do {
1018 if (!strcmp((char*) (s + 2), q + 1)) {
1019 break;
1021 s += 2 + *s; /* TODO - fix this throughout */
1022 } while (*s);
1023 if (!*s) {
1024 return 0;
1026 assert(q - p < sizeof(buf));
1027 memcpy(buf, p, q-p);
1028 buf[q-p] = 0;
1029 buf[2] = s[1];
1030 p = buf;
1032 #endif
1034 lang_cult = codeset = 0; /* Assume C and default codeset. */
1035 if (((*p == 'C') && !p[1]) || !strcmp(p, posix)) {
1036 goto FIND_LOCALE;
1039 if ((strlen(p) > 5) && (p[5] == '.')) { /* Codeset in locale name? */
1040 /* TODO: maybe CODESET_LIST + *s ??? */
1041 /* 7bit is 1, UTF-8 is 2, 8-bit is >= 3 */
1042 codeset = 2;
1043 if (strcasecmp(utf8, p + 6) != 0) {/* TODO - fix! */
1044 s = CODESET_LIST;
1045 do {
1046 ++codeset; /* Increment codeset first. */
1047 if (!strcmp((char*) CODESET_LIST + *s, p + 6)) {
1048 goto FIND_LANG_CULT;
1050 } while (*++s);
1051 return 0; /* No matching codeset! */
1055 FIND_LANG_CULT: /* Find language_culture number. */
1056 s = LOCALE_NAMES;
1057 do { /* TODO -- do a binary search? */
1058 /* TODO -- fix gen_mmap!*/
1059 ++lang_cult; /* Increment first since C/POSIX is 0. */
1060 if (!strncmp((char*) s, p, 5)) { /* Found a matching locale name; */
1061 goto FIND_LOCALE;
1063 s += 5;
1064 } while (lang_cult < __LOCALE_DATA_NUM_LOCALE_NAMES);
1065 return 0; /* No matching language_culture! */
1067 FIND_LOCALE: /* Find locale row matching name and codeset */
1068 s = LOCALES;
1069 n = 0;
1070 do { /* TODO -- do a binary search? */
1071 if ((lang_cult == *s) && ((codeset == s[1]) || (codeset == s[2]))) {
1072 i = 1;
1073 s = new_locale + 1;
1074 do {
1075 if (category_mask & i) {
1076 /* Encode current locale row number. */
1077 ((unsigned char *) s)[0] = (n >> 7) | 0x80;
1078 ((unsigned char *) s)[1] = (n & 0x7f) | 0x80;
1080 s += 2;
1081 i += i;
1082 } while (i < (1 << LC_ALL));
1084 return i; /* Return non-zero */
1086 s += __LOCALE_DATA_WIDTH_LOCALES;
1087 ++n;
1088 } while (n <= __LOCALE_DATA_NUM_LOCALES); /* We started at 1!!! */
1090 return 0; /* Unsupported locale. */
1093 static unsigned char *composite_locale(int category_mask, const char *locale,
1094 unsigned char *new_locale)
1096 char buf[MAX_LOCALE_STR];
1097 char *t;
1098 char *e;
1099 int c;
1100 int component_mask;
1102 if (!strchr(locale,'=')) {
1103 if (!find_locale(category_mask, locale, new_locale)) {
1104 return NULL;
1106 return new_locale;
1109 if (strlen(locale) >= sizeof(buf)) {
1110 return NULL;
1112 stpcpy(buf, locale);
1114 component_mask = 0;
1115 t = strtok_r(buf, "=", &e); /* This can't fail because of strchr test above. */
1116 do {
1117 c = 0;
1118 /* CATEGORY_NAMES is unsigned char* */
1119 while (strcmp((char*) CATEGORY_NAMES + (int) CATEGORY_NAMES[c], t)) {
1120 if (++c == LC_ALL) { /* Unknown category name! */
1121 return NULL;
1124 t = strtok_r(NULL, ";", &e);
1125 c = (1 << c);
1126 if (component_mask & c) { /* Multiple components for one category. */
1127 return NULL;
1129 component_mask |= c;
1130 if ((category_mask & c) && (!t || !find_locale(c, t, new_locale))) {
1131 return NULL;
1133 } while ((t = strtok_r(NULL, "=", &e)) != NULL);
1135 if (category_mask & ~component_mask) { /* Category component(s) missing. */
1136 return NULL;
1139 return new_locale;
1142 __locale_t newlocale(int category_mask, const char *locale, __locale_t base)
1144 const char *p;
1145 int i, j, k;
1146 unsigned char new_selector[LOCALE_SELECTOR_SIZE];
1148 if (category_mask == (1 << LC_ALL)) {
1149 category_mask = LC_ALL_MASK;
1152 if (!locale || ((unsigned)(category_mask) > LC_ALL_MASK)) {
1153 INVALID:
1154 __set_errno(EINVAL);
1155 return NULL; /* No locale or illegal/unsupported category. */
1158 strcpy((char *) new_selector,
1159 (base ? (char *) base->cur_locale : C_LOCALE_SELECTOR));
1161 if (!locale[0]) { /* locale == "", so check environment. */
1162 const char *envstr[4];
1164 envstr[0] = "LC_ALL";
1165 envstr[1] = NULL;
1166 envstr[2] = "LANG";
1167 envstr[3] = posix;
1169 i = 1;
1170 k = 0;
1171 do {
1172 if (category_mask & i) {
1173 /* Note: SUSv3 doesn't define a fallback mechanism here.
1174 * So, if LC_ALL is invalid, we do _not_ continue trying
1175 * the other environment vars. */
1176 envstr[1] = (char*) CATEGORY_NAMES + CATEGORY_NAMES[k];
1177 j = 0;
1178 while (1) {
1179 p = envstr[j];
1180 if (++j >= 4)
1181 break; /* now p == "POSIX" */
1182 p = getenv(p);
1183 if (p && p[0])
1184 break;
1187 /* The user set something... is it valid? */
1188 /* Note: Since we don't support user-supplied locales and
1189 * alternate paths, we don't need to worry about special
1190 * handling for suid/sgid apps. */
1191 if (!find_locale(i, p, new_selector)) {
1192 goto INVALID;
1195 i += i;
1196 } while (++k < LC_ALL);
1197 } else if (!composite_locale(category_mask, locale, new_selector)) {
1198 goto INVALID;
1202 /* If we get here, the new selector corresponds to a valid locale. */
1204 #if 0
1205 if (base) {
1206 _locale_set_l(new_selector, base);
1207 } else {
1208 base = _locale_new(new_selector);
1210 #else
1211 if (!base) {
1212 base = calloc(1, sizeof(struct __uclibc_locale_struct));
1213 if (base == NULL)
1214 return base;
1215 _locale_init_l(base);
1218 _locale_set_l(new_selector, base);
1219 #endif
1221 return base;
1223 #ifdef __UCLIBC_HAS_XLOCALE__
1224 libc_hidden_def(newlocale)
1225 #endif
1227 #endif
1228 /**********************************************************************/
1229 #ifdef L_duplocale
1232 __locale_t duplocale(__locale_t dataset)
1234 __locale_t r;
1235 uint16_t * i2w;
1236 size_t n;
1238 assert(dataset != LC_GLOBAL_LOCALE);
1240 r = malloc(sizeof(struct __uclibc_locale_struct));
1241 if (r != NULL) {
1242 n = 2 * dataset->collate.max_col_index + 2;
1243 i2w = calloc(n, sizeof(uint16_t));
1244 if (i2w != NULL) {
1245 memcpy(r, dataset, sizeof(struct __uclibc_locale_struct));
1246 r->collate.index2weight = i2w;
1247 memcpy(i2w, dataset->collate.index2weight, n * sizeof(uint16_t));
1248 } else {
1249 free(r);
1250 r = NULL;
1253 return r;
1256 #endif
1257 /**********************************************************************/
1258 #ifdef L_freelocale
1260 void freelocale(__locale_t dataset)
1262 assert(dataset != __global_locale);
1263 assert(dataset != LC_GLOBAL_LOCALE);
1265 free(dataset->collate.index2weight); /* Free collation data. */
1266 free(dataset); /* Free locale */
1269 #endif
1270 /**********************************************************************/
1271 #ifdef L_uselocale
1273 __locale_t uselocale(__locale_t dataset)
1275 __locale_t old;
1277 if (!dataset) {
1278 old = __UCLIBC_CURLOCALE;
1279 } else {
1280 if (dataset == LC_GLOBAL_LOCALE) {
1281 dataset = __global_locale;
1283 #ifdef __UCLIBC_HAS_THREADS__
1284 old = __curlocale_set(dataset);
1285 #else
1286 old = __curlocale_var;
1287 __curlocale_var = dataset;
1288 #endif
1291 if (old == __global_locale) {
1292 return LC_GLOBAL_LOCALE;
1294 return old;
1296 libc_hidden_def(uselocale)
1298 #endif
1299 /**********************************************************************/
1300 #ifdef L___curlocale
1302 #ifdef __UCLIBC_HAS_THREADS__
1304 __locale_t weak_const_function __curlocale(void)
1306 return __curlocale_var; /* This is overriden by the thread version. */
1308 libc_hidden_weak(__curlocale)
1310 __locale_t weak_function __curlocale_set(__locale_t newloc)
1312 __locale_t oldloc = __curlocale_var;
1313 assert(newloc != LC_GLOBAL_LOCALE);
1314 __curlocale_var = newloc;
1315 return oldloc;
1317 libc_hidden_weak(__curlocale_set)
1319 #endif
1321 #endif
1322 /**********************************************************************/
1323 #ifdef L___locale_mbrtowc_l
1325 /* NOTE: This returns an int... not size_t. Also, it is not a general
1326 * routine. It is actually a very stripped-down version of mbrtowc
1327 * that takes a __locale_t arg. This is used by strcoll and strxfrm.
1328 * It is also used above to generate wchar_t versions of the decimal point
1329 * and thousands seperator. */
1332 #ifndef __CTYPE_HAS_UTF_8_LOCALES
1333 #warning __CTYPE_HAS_UTF_8_LOCALES not set!
1334 #endif
1335 #ifndef __CTYPE_HAS_8_BIT_LOCALES
1336 #warning __CTYPE_HAS_8_BIT_LOCALES not set!
1337 #endif
1339 #define Cc2wc_IDX_SHIFT __LOCALE_DATA_Cc2wc_IDX_SHIFT
1340 #define Cc2wc_ROW_LEN __LOCALE_DATA_Cc2wc_ROW_LEN
1342 extern size_t _wchar_utf8sntowcs(wchar_t *__restrict pwc, size_t wn,
1343 const char **__restrict src, size_t n,
1344 mbstate_t *ps, int allow_continuation) attribute_hidden;
1346 int attribute_hidden __locale_mbrtowc_l(wchar_t *__restrict dst,
1347 const char *__restrict src,
1348 __locale_t loc )
1350 #ifdef __CTYPE_HAS_UTF_8_LOCALES
1351 if (loc->encoding == __ctype_encoding_utf8) {
1352 mbstate_t ps;
1353 const char *p = src;
1354 size_t r;
1355 ps.__mask = 0;
1356 r = _wchar_utf8sntowcs(dst, 1, &p, SIZE_MAX, &ps, 1);
1357 return (r == 1) ? (p-src) : r; /* Need to return 0 if nul char. */
1359 #endif
1361 #ifdef __CTYPE_HAS_8_BIT_LOCALES
1362 assert((loc->encoding == __ctype_encoding_7_bit) || (loc->encoding == __ctype_encoding_8_bit));
1363 #else
1364 assert(loc->encoding == __ctype_encoding_7_bit);
1365 #endif
1367 if ((*dst = ((unsigned char)(*src))) < 0x80) { /* ASCII... */
1368 return (*src != 0);
1371 #ifdef __CTYPE_HAS_8_BIT_LOCALES
1372 if (loc->encoding == __ctype_encoding_8_bit) {
1373 wchar_t wc = *dst - 0x80;
1374 *dst = loc->tbl8c2wc[
1375 (loc->idx8c2wc[wc >> Cc2wc_IDX_SHIFT]
1376 << Cc2wc_IDX_SHIFT) + (wc & (Cc2wc_ROW_LEN - 1))];
1377 if (*dst) {
1378 return 1;
1381 #endif
1383 return -1;
1386 #endif
1387 /**********************************************************************/