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/>.
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.
26 * Added in collation support and updated stub nl_langinfo.
29 * Added glibc-like extended locale stuff (newlocale, duplocale, etc).
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.
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++.
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
61 #ifdef __UCLIBC_MJN3_ONLY__
63 #warning TODO: Make the link_warning()s a config option?
67 #define link_warning(A,B)
69 #undef __LOCALE_C_ONLY
70 #ifndef __UCLIBC_HAS_LOCALE__
71 #define __LOCALE_C_ONLY
72 #endif /* __UCLIBC_HAS_LOCALE__ */
75 #ifdef __LOCALE_C_ONLY
79 #else /* __LOCALE_C_ONLY */
81 #ifdef __UCLIBC_MJN3_ONLY__
83 #warning TODO: Fix the __CTYPE_HAS_8_BIT_LOCALES define at the top of the file.
84 #warning TODO: Fix __WCHAR_ENABLED.
88 /* Need to include this before locale.h! */
89 #include <bits/uClibc_locale.h>
92 #define CODESET_LIST (__locale_mmap->codeset_list)
94 #ifdef __UCLIBC_HAS_XLOCALE__
96 #else /* __UCLIBC_HAS_XLOCALE__ */
97 /* We need this internally... */
98 #define __UCLIBC_HAS_XLOCALE__ 1
100 #undef __UCLIBC_HAS_XLOCALE__
101 #endif /* __UCLIBC_HAS_XLOCALE__ */
105 #define LOCALE_NAMES (__locale_mmap->locale_names5)
106 #define LOCALES (__locale_mmap->locales)
107 #define LOCALE_AT_MODIFIERS (__locale_mmap->locale_at_modifiers)
108 #define CATEGORY_NAMES (__locale_mmap->lc_names)
110 #ifdef __UCLIBC_MJN3_ONLY__
111 #warning REMINDER: redo the MAX_LOCALE_STR stuff...
113 #define MAX_LOCALE_STR 256 /* TODO: Only sufficient for current case. */
114 #define MAX_LOCALE_CATEGORY_STR 32 /* TODO: Only sufficient for current case. */
115 /* Note: Best if MAX_LOCALE_CATEGORY_STR is a power of 2. */
117 extern int _locale_set_l(const unsigned char *p, __locale_t base) attribute_hidden;
118 extern void _locale_init_l(__locale_t base) attribute_hidden;
120 #endif /* __LOCALE_C_ONLY */
122 #undef LOCALE_STRING_SIZE
123 #define LOCALE_SELECTOR_SIZE (2 * __LC_ALL + 2)
125 #ifdef __UCLIBC_MJN3_ONLY__
127 #warning TODO: Create a C locale selector string.
130 #define C_LOCALE_SELECTOR "\x23\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80"
133 #include <langinfo.h>
134 #include <nl_types.h>
136 /**********************************************************************/
139 #ifdef __LOCALE_C_ONLY
141 link_warning(setlocale,"REMINDER: The 'setlocale' function supports only C|POSIX locales.")
143 static const char C_string[] = "C";
145 char *setlocale(int category, register const char *locale)
147 return ( (((unsigned int)(category)) <= LC_ALL)
148 && ( (!locale) /* Request for locale category string. */
149 || (!*locale) /* Implementation-defined default is C. */
150 || ((*locale == 'C') && !locale[1])
151 || (!strcmp(locale, "POSIX"))) )
152 ? (char *) C_string /* Always in C/POSIX locale. */
156 #else /* ---------------------------------------------- __LOCALE_C_ONLY */
158 #ifdef __UCLIBC_HAS_THREADS__
159 link_warning(setlocale,"REMINDER: The 'setlocale' function is _not_ threadsafe except for simple queries.")
162 #if !defined(__LOCALE_DATA_NUM_LOCALES) || (__LOCALE_DATA_NUM_LOCALES <= 1)
163 #error locales enabled, but not data other than for C locale!
166 #ifdef __UCLIBC_MJN3_ONLY__
167 #warning TODO: Move posix and utf8 strings.
169 static const char posix[] = "POSIX";
170 static const char utf8[] = "UTF-8";
172 #ifdef __UCLIBC_MJN3_ONLY__
173 #warning TODO: Fix dimensions of hr_locale.
175 /* Individual category strings start at hr_locale + category * MAX_LOCALE_CATEGORY.
176 * This holds for LC_ALL as well.
178 static char hr_locale[(MAX_LOCALE_CATEGORY_STR * LC_ALL) + MAX_LOCALE_STR];
181 static void update_hr_locale(const unsigned char *spec)
183 const unsigned char *loc;
184 const unsigned char *s;
186 int i, category, done;
191 n = hr_locale + category * MAX_LOCALE_CATEGORY_STR;
193 if (category == LC_ALL) {
195 for (i = 0 ; i < LC_ALL-1 ; i += 2) {
196 if ((s[i] != s[i+2]) || (s[i+1] != s[i+3])) {
200 /* All categories the same, so simplify string by using a single
206 i = (category == LC_ALL) ? 0 : category;
210 if ((*s != 0xff) || (s[1] != 0xff)) {
212 + __LOCALE_DATA_WIDTH_LOCALES * ((((int)(*s & 0x7f)) << 7)
214 if (category == LC_ALL) {
215 /* CATEGORY_NAMES is unsigned char* */
216 n = stpcpy(n, (char*) CATEGORY_NAMES + (int) CATEGORY_NAMES[i]);
224 memcpy(n, LOCALE_NAMES + 5*((*loc)-1), 5);
233 } else if (loc[2] >= 3) {
234 n = stpcpy(n, (char*) CODESET_LIST + (int)(CODESET_LIST[loc[2] - 3]));
239 q = (char*) LOCALE_AT_MODIFIERS;
252 } while (++i < category);
253 *--n = 0; /* Remove trailing ';' and nul-terminate. */
259 char *setlocale(int category, const char *locale)
261 if (((unsigned int)(category)) > LC_ALL) {
263 __set_errno(EINVAL); /* glibc sets errno -- SUSv3 doesn't say. */
265 return NULL; /* Illegal/unsupported category. */
268 if (locale != NULL) { /* Not just a query... */
269 if (!newlocale((1 << category), locale, __global_locale)) {
270 return NULL; /* Failed! */
272 update_hr_locale(__global_locale->cur_locale);
275 /* Either a query or a successful set, so return current locale string. */
276 return hr_locale + (category * MAX_LOCALE_CATEGORY_STR);
279 #endif /* __LOCALE_C_ONLY */
282 /**********************************************************************/
285 /* Note: We assume here that the compiler does the sane thing regarding
286 * placement of the fields in the struct. If necessary, we could ensure
287 * this usings an array of offsets but at some size cost. */
290 #ifdef __LOCALE_C_ONLY
292 link_warning(localeconv,"REMINDER: The 'localeconv' function is hardwired for C/POSIX locale only.")
294 static struct lconv the_lconv;
296 static const char decpt[] = ".";
298 struct lconv *localeconv(void)
300 register char *p = (char *)(&the_lconv);
302 *((char **)p) = (char *) decpt;
304 p += sizeof(char **);
305 *((char **)p) = (char *) (decpt+1);
306 } while (p < (char *) &the_lconv.negative_sign);
308 p = (&the_lconv.int_frac_digits);
312 } while (p <= &the_lconv.int_n_sign_posn);
317 #else /* __LOCALE_C_ONLY */
319 static struct lconv the_lconv;
321 struct lconv *localeconv(void)
323 register char *p = (char *) &the_lconv;
324 register char **q = (char **) &(__UCLIBC_CURLOCALE->decimal_point);
328 p += sizeof(char **);
330 } while (p < &the_lconv.int_frac_digits);
336 } while (p <= &the_lconv.int_n_sign_posn);
341 #endif /* __LOCALE_C_ONLY */
343 libc_hidden_def(localeconv)
346 /**********************************************************************/
347 #if defined(L__locale_init) && !defined(__LOCALE_C_ONLY)
349 struct __uclibc_locale_struct __global_locale_data;
351 __locale_t __global_locale = &__global_locale_data;
353 #ifdef __UCLIBC_HAS_XLOCALE__
354 __locale_t __curlocale_var = &__global_locale_data;
357 /*----------------------------------------------------------------------*/
358 #ifdef __UCLIBC_MJN3_ONLY__
359 #warning TODO: Move utf8 and ascii strings.
361 static const char utf8[] = "UTF-8";
362 static const char ascii[] = "ASCII";
367 uint16_t MAX_WEIGHTS;
368 uint16_t num_index2weight;
369 #define num_index2ruleidx num_index2weight
370 uint16_t num_weightstr;
371 uint16_t num_multistart;
372 uint16_t num_override;
373 uint16_t num_ruletable;
377 uint16_t num_weights;
378 uint16_t num_starters;
384 uint16_t num_col_base;
385 uint16_t max_col_index;
386 uint16_t undefined_idx;
388 uint16_t range_count;
389 uint16_t range_base_weight;
390 uint16_t range_rule_offset;
392 uint16_t index2weight_offset;
393 uint16_t index2ruleidx_offset;
394 uint16_t multistart_offset;
395 uint16_t wcs2colidt_offset_low;
396 uint16_t wcs2colidt_offset_hi;
401 uint16_t undefined_idx;
402 uint16_t overrides_offset;
403 uint16_t multistart_offset;
406 static int init_cur_collate(int der_num, __collate_t *cur_collate)
408 const uint16_t *__locale_collate_tbl = __locale_mmap->collate_data;
409 coldata_header_t *cdh;
416 #ifdef __UCLIBC_MJN3_ONLY__
417 #warning kill of x86-specific asserts
420 assert(sizeof(coldata_base_t) == 19*2);
421 assert(sizeof(coldata_der_t) == 4*2);
422 assert(sizeof(coldata_header_t) == 8*2);
425 if (!der_num) { /* C locale... special */
426 cur_collate->num_weights = 0;
432 cdh = (coldata_header_t *) __locale_collate_tbl;
434 #ifdef __UCLIBC_MJN3_ONLY__
435 #warning CONSIDER: Should we assert here?
438 if (der_num >= cdh->num_der) {
442 assert((der_num < cdh->num_der));
445 cdd = (coldata_der_t *)(__locale_collate_tbl
446 + (sizeof(coldata_header_t)
447 + cdh->num_base * sizeof(coldata_base_t)
448 + der_num * sizeof(coldata_der_t)
451 cdb = (coldata_base_t *)(__locale_collate_tbl
452 + (sizeof(coldata_header_t)
453 + cdd->base_idx * sizeof(coldata_base_t)
456 memcpy(cur_collate, cdb, offsetof(coldata_base_t,index2weight_offset));
457 cur_collate->undefined_idx = cdd->undefined_idx;
459 cur_collate->ti_mask = (1 << cur_collate->ti_shift)-1;
460 cur_collate->ii_mask = (1 << cur_collate->ii_shift)-1;
462 /* fflush(stdout); */
463 /* fprintf(stderr,"base=%d num_col_base: %d %d\n", cdd->base_idx ,cur_collate->num_col_base, cdb->num_col_base); */
465 n = (sizeof(coldata_header_t) + cdh->num_base * sizeof(coldata_base_t)
466 + cdh->num_der * sizeof(coldata_der_t))/2;
468 /* fprintf(stderr,"n = %d\n", n); */
469 cur_collate->index2weight_tbl = __locale_collate_tbl + n + cdb->index2weight_offset;
470 /* fprintf(stderr,"i2w = %d\n", n + cdb->index2weight_offset); */
471 n += cdh->num_index2weight;
472 cur_collate->index2ruleidx_tbl = __locale_collate_tbl + n + cdb->index2ruleidx_offset;
473 /* fprintf(stderr,"i2r = %d\n", n + cdb->index2ruleidx_offset); */
474 n += cdh->num_index2ruleidx;
475 cur_collate->multistart_tbl = __locale_collate_tbl + n + cdd->multistart_offset;
476 /* fprintf(stderr,"mts = %d\n", n + cdb->multistart_offset); */
477 n += cdh->num_multistart;
478 cur_collate->overrides_tbl = __locale_collate_tbl + n + cdd->overrides_offset;
479 /* fprintf(stderr,"ovr = %d\n", n + cdd->overrides_offset); */
480 n += cdh->num_override;
481 cur_collate->ruletable = __locale_collate_tbl + n;
482 /* fprintf(stderr, "rtb = %d\n", n); */
483 n += cdh->num_ruletable;
484 cur_collate->weightstr = __locale_collate_tbl + n;
485 /* fprintf(stderr,"wts = %d\n", n); */
486 n += cdh->num_weightstr;
487 cur_collate->wcs2colidt_tbl = __locale_collate_tbl + n
488 + (((unsigned long)(cdb->wcs2colidt_offset_hi)) << 16)
489 + cdb->wcs2colidt_offset_low;
490 /* fprintf(stderr,"wcs = %lu\n", n + (((unsigned long)(cdb->wcs2colidt_offset_hi)) << 16) */
491 /* + cdb->wcs2colidt_offset_low); */
493 cur_collate->MAX_WEIGHTS = cdh->MAX_WEIGHTS;
495 #ifdef __UCLIBC_MJN3_ONLY__
496 #warning CONSIDER: Fix the +1 by increasing max_col_index?
497 #warning CONSIDER: Since this collate info is dependent only on LC_COLLATE ll_cc and not on codeset, we could just globally allocate this for each in a table
500 cur_collate->index2weight = calloc(2*cur_collate->max_col_index+2,
502 if (!cur_collate->index2weight) {
505 cur_collate->index2ruleidx = cur_collate->index2weight
506 + cur_collate->max_col_index + 1;
508 memcpy(cur_collate->index2weight, cur_collate->index2weight_tbl,
509 cur_collate->num_col_base * sizeof(uint16_t));
510 memcpy(cur_collate->index2ruleidx, cur_collate->index2ruleidx_tbl,
511 cur_collate->num_col_base * sizeof(uint16_t));
513 /* now do the overrides */
514 p = cur_collate->overrides_tbl;
516 /* fprintf(stderr, "processing override -- count = %d\n", *p); */
521 /* fprintf(stderr, " i=%d (%#x) w=%d *p=%d\n", i, i, w, *p); */
522 cur_collate->index2weight[i-1] = w++;
523 cur_collate->index2ruleidx[i-1] = *p++;
529 /* fprintf(stderr, " i=%d (%#x) w=%d *p=%d\n", i, i, p[1], p[2]); */
530 cur_collate->index2weight[i-1] = *++p;
531 cur_collate->index2ruleidx[i-1] = *++p;
535 for (i=0 ; i < cur_collate->multistart_tbl[0] ; i++) {
536 p = cur_collate->multistart_tbl;
537 /* fprintf(stderr, "%2d of %2d: %d ", i, cur_collate->multistart_tbl[0], p[i]); */
543 if (!*p) { /* found it */
544 /* fprintf(stderr, "found: n=%d (%#lx) |%.*ls|\n", n, (int) *cs->s, n, cs->s); */
545 /* fprintf(stderr, ": %d - single\n", n); */
548 /* the lookup check here is safe since we're assured that *p is a valid colidex */
549 /* fprintf(stderr, "lookup(%lc)==%d *p==%d\n", cs->s[n], lookup(cs->s[n]), (int) *p); */
550 /* fprintf(stderr, ": %d - ", n); */
552 /* fprintf(stderr, "%d|", *p); */
564 int attribute_hidden _locale_set_l(const unsigned char *p, __locale_t base)
567 unsigned char *s = base->cur_locale + 1;
569 const unsigned char *r;
572 const unsigned char *d;
573 int row; /* locale row */
574 int crow; /* category row */
582 newcol.index2weight = NULL;
583 if ((p[2*LC_COLLATE] != s[2*LC_COLLATE])
584 || (p[2*LC_COLLATE + 1] != s[2*LC_COLLATE + 1])
586 row = (((int)(*p & 0x7f)) << 7) + (p[1] & 0x7f);
587 assert(row < __LOCALE_DATA_NUM_LOCALES);
588 if (!init_cur_collate(__locale_mmap->locales[ __LOCALE_DATA_WIDTH_LOCALES
589 * row + 3 + LC_COLLATE ],
592 return 0; /* calloc failed. */
594 free(base->collate.index2weight);
595 memcpy(&base->collate, &newcol, sizeof(__collate_t));
599 if ((*p != *s) || (p[1] != s[1])) {
600 row = (((int)(*p & 0x7f)) << 7) + (p[1] & 0x7f);
601 assert(row < __LOCALE_DATA_NUM_LOCALES);
606 if ((i != LC_COLLATE)
607 && ((len = __locale_mmap->lc_common_item_offsets_LEN[i]) != 0)
609 crow = __locale_mmap->locales[ __LOCALE_DATA_WIDTH_LOCALES * row
613 x = (const char **)(((char *) base)
614 + base->category_offsets[i]);
616 stp = __locale_mmap->lc_common_tbl_offsets + 4*i;
617 r = (const unsigned char *)( ((char *)__locale_mmap) + *stp );
618 io = (const uint16_t *)( ((char *)__locale_mmap) + *++stp );
619 ii = (const uint16_t *)( ((char *)__locale_mmap) + *++stp );
620 d = (const unsigned char *)( ((char *)__locale_mmap) + *++stp );
621 for (c = 0; c < len; c++) {
622 x[c] = (char*)(d + ii[r[crow + c] + io[c]]);
626 c = __locale_mmap->locales[ __LOCALE_DATA_WIDTH_LOCALES * row
630 base->codeset = utf8;
631 base->encoding = __ctype_encoding_utf8;
632 /* TODO - fix for bcc */
633 base->mb_cur_max = 6;
636 base->codeset = ascii;
637 base->encoding = __ctype_encoding_7_bit;
638 base->mb_cur_max = 1;
641 const __codeset_8_bit_t *c8b;
644 base->codeset = (char *) (r + r[c]);
645 base->encoding = __ctype_encoding_8_bit;
646 #ifdef __UCLIBC_MJN3_ONLY__
647 #warning REMINDER: update 8 bit mb_cur_max when translit implemented!
649 /* TODO - update when translit implemented! */
650 base->mb_cur_max = 1;
651 c8b = __locale_mmap->codeset_8_bit + c;
652 #ifdef __CTYPE_HAS_8_BIT_LOCALES
653 base->idx8ctype = c8b->idx8ctype;
654 base->idx8uplow = c8b->idx8uplow;
655 #ifdef __UCLIBC_HAS_WCHAR__
656 base->idx8c2wc = c8b->idx8c2wc;
657 base->idx8wc2c = c8b->idx8wc2c;
659 #endif /* __UCLIBC_HAS_WCHAR__ */
661 /* What follows is fairly bloated, but it is just a hack
662 * to get the 8-bit codeset ctype stuff functioning.
663 * All of this will be replaced in the next generation
664 * of locale support anyway... */
666 memcpy(base->__ctype_b_data,
667 __C_ctype_b - __UCLIBC_CTYPE_B_TBL_OFFSET,
668 (256 + __UCLIBC_CTYPE_B_TBL_OFFSET)
669 * sizeof(__ctype_mask_t));
670 memcpy(base->__ctype_tolower_data,
671 __C_ctype_tolower - __UCLIBC_CTYPE_TO_TBL_OFFSET,
672 (256 + __UCLIBC_CTYPE_TO_TBL_OFFSET)
673 * sizeof(__ctype_touplow_t));
674 memcpy(base->__ctype_toupper_data,
675 __C_ctype_toupper - __UCLIBC_CTYPE_TO_TBL_OFFSET,
676 (256 + __UCLIBC_CTYPE_TO_TBL_OFFSET)
677 * sizeof(__ctype_touplow_t));
679 #define Cctype_TBL_MASK ((1 << __LOCALE_DATA_Cctype_IDX_SHIFT) - 1)
680 #define Cctype_IDX_OFFSET (128 >> __LOCALE_DATA_Cctype_IDX_SHIFT)
686 for (u=0 ; u < 128 ; u++) {
687 #ifdef __LOCALE_DATA_Cctype_PACKED
689 [ ((int)(c8b->idx8ctype
690 [(u >> __LOCALE_DATA_Cctype_IDX_SHIFT) ])
691 << (__LOCALE_DATA_Cctype_IDX_SHIFT - 1))
692 + ((u & Cctype_TBL_MASK) >> 1)];
693 c = (u & 1) ? (c >> 4) : (c & 0xf);
696 [ ((int)(c8b->idx8ctype
697 [(u >> __LOCALE_DATA_Cctype_IDX_SHIFT) ])
698 << __LOCALE_DATA_Cctype_IDX_SHIFT)
699 + (u & Cctype_TBL_MASK) ];
702 m = base->code2flag[c];
705 [128 + __UCLIBC_CTYPE_B_TBL_OFFSET + u]
708 #ifdef __UCLIBC_HAS_CTYPE_SIGNED__
709 if (((signed char)(128 + u)) != -1) {
710 base->__ctype_b_data[__UCLIBC_CTYPE_B_TBL_OFFSET
711 + ((signed char)(128 + u))]
716 base->__ctype_tolower_data
717 [128 + __UCLIBC_CTYPE_TO_TBL_OFFSET + u]
719 base->__ctype_toupper_data
720 [128 + __UCLIBC_CTYPE_TO_TBL_OFFSET + u]
723 if (m & (_ISlower|_ISupper)) {
725 [ ((int)(c8b->idx8uplow
726 [u >> __LOCALE_DATA_Cuplow_IDX_SHIFT])
727 << __LOCALE_DATA_Cuplow_IDX_SHIFT)
729 & ((1 << __LOCALE_DATA_Cuplow_IDX_SHIFT)
732 base->__ctype_toupper_data
733 [128 + __UCLIBC_CTYPE_TO_TBL_OFFSET + u]
734 = (unsigned char)(128 + u + c);
735 #ifdef __UCLIBC_HAS_CTYPE_SIGNED__
736 if (((signed char)(128 + u)) != -1) {
737 base->__ctype_toupper_data
738 [__UCLIBC_CTYPE_TO_TBL_OFFSET
739 + ((signed char)(128 + u))]
740 = (unsigned char)(128 + u + c);
744 base->__ctype_tolower_data
745 [128 + __UCLIBC_CTYPE_TO_TBL_OFFSET + u]
746 = (unsigned char)(128 + u - c);
747 #ifdef __UCLIBC_HAS_CTYPE_SIGNED__
748 if (((signed char)(128 + u)) != -1) {
749 base->__ctype_tolower_data
750 [__UCLIBC_CTYPE_TO_TBL_OFFSET
751 + ((signed char)(128 + u))]
752 = (unsigned char)(128 + u - c);
760 #ifdef __UCLIBC_HAS_XLOCALE__
761 base->__ctype_b = base->__ctype_b_data
762 + __UCLIBC_CTYPE_B_TBL_OFFSET;
763 base->__ctype_tolower = base->__ctype_tolower_data
764 + __UCLIBC_CTYPE_TO_TBL_OFFSET;
765 base->__ctype_toupper = base->__ctype_toupper_data
766 + __UCLIBC_CTYPE_TO_TBL_OFFSET;
767 #else /* __UCLIBC_HAS_XLOCALE__ */
768 __ctype_b = base->__ctype_b_data
769 + __UCLIBC_CTYPE_B_TBL_OFFSET;
770 __ctype_tolower = base->__ctype_tolower_data
771 + __UCLIBC_CTYPE_TO_TBL_OFFSET;
772 __ctype_toupper = base->__ctype_toupper_data
773 + __UCLIBC_CTYPE_TO_TBL_OFFSET;
774 #endif /* __UCLIBC_HAS_XLOCALE__ */
776 #endif /* __CTYPE_HAS_8_BIT_LOCALES */
778 #ifdef __UCLIBC_MJN3_ONLY__
779 #warning TODO: Put the outdigit string length in the locale_mmap object.
781 d = base->outdigit_length;
782 x = &base->outdigit0_mb;
783 for (c = 0 ; c < 10 ; c++) {
784 ((unsigned char *)d)[c] = strlen(x[c]);
787 } else if (i == LC_NUMERIC) {
788 assert(LC_NUMERIC > LC_CTYPE); /* Need ctype initialized. */
790 base->decimal_point_len
791 = __locale_mbrtowc_l(&base->decimal_point_wc,
792 base->decimal_point, base);
793 assert(base->decimal_point_len > 0);
794 assert(base->decimal_point[base->decimal_point_len] == 0);
796 if (*base->grouping) {
797 base->thousands_sep_len
798 = __locale_mbrtowc_l(&base->thousands_sep_wc,
799 base->thousands_sep, base);
801 #ifdef __UCLIBC_MJN3_ONLY__
802 #warning TODO: Remove hack involving grouping without a thousep char (bg_BG).
804 assert(base->thousands_sep_len >= 0);
805 if (base->thousands_sep_len == 0) {
806 base->grouping = base->thousands_sep; /* empty string */
808 assert(base->thousands_sep[base->thousands_sep_len] == 0);
810 assert(base->thousands_sep_len > 0);
811 assert(base->thousands_sep[base->thousands_sep_len] == 0);
815 /* } else if (i == LC_COLLATE) { */
816 /* init_cur_collate(__locale_mmap->locales[ __LOCALE_DATA_WIDTH_LOCALES */
817 /* * row + 3 + i ], */
818 /* &base->collate); */
824 } while (i < LC_ALL);
829 static const uint16_t __code2flag[16] = {
830 0, /* unclassified = 0 */
831 _ISprint|_ISgraph|_ISalnum|_ISalpha, /* alpha_nonupper_nonlower */
832 _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, /* alpha_lower */
833 _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower|_ISupper, /* alpha_upper_lower */
834 _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, /* alpha_upper */
835 _ISprint|_ISgraph|_ISalnum|_ISdigit, /* digit */
836 _ISprint|_ISgraph|_ISpunct, /* punct */
837 _ISprint|_ISgraph, /* graph */
838 _ISprint|_ISspace, /* print_space_nonblank */
839 _ISprint|_ISspace|_ISblank, /* print_space_blank */
840 _ISspace, /* space_nonblank_noncntrl */
841 _ISspace|_ISblank, /* space_blank_noncntrl */
842 _IScntrl|_ISspace, /* cntrl_space_nonblank */
843 _IScntrl|_ISspace|_ISblank, /* cntrl_space_blank */
844 _IScntrl /* cntrl_nonspace */
847 void attribute_hidden _locale_init_l(__locale_t base)
849 memset(base->cur_locale, 0, LOCALE_SELECTOR_SIZE);
850 base->cur_locale[0] = '#';
852 memcpy(base->category_item_count,
853 __locale_mmap->lc_common_item_offsets_LEN,
856 ++base->category_item_count[0]; /* Increment for codeset entry. */
857 base->category_offsets[0] = offsetof(struct __uclibc_locale_struct, outdigit0_mb);
858 base->category_offsets[1] = offsetof(struct __uclibc_locale_struct, decimal_point);
859 base->category_offsets[2] = offsetof(struct __uclibc_locale_struct, int_curr_symbol);
860 base->category_offsets[3] = offsetof(struct __uclibc_locale_struct, abday_1);
861 /* base->category_offsets[4] = offsetof(struct __uclibc_locale_struct, collate???); */
862 base->category_offsets[5] = offsetof(struct __uclibc_locale_struct, yesexpr);
864 #ifdef __CTYPE_HAS_8_BIT_LOCALES
866 = (const unsigned char *) &__locale_mmap->tbl8ctype;
868 = (const unsigned char *) &__locale_mmap->tbl8uplow;
869 #ifdef __UCLIBC_HAS_WCHAR__
871 = (const uint16_t *) &__locale_mmap->tbl8c2wc;
873 = (const unsigned char *) &__locale_mmap->tbl8wc2c;
875 #endif /* __UCLIBC_HAS_WCHAR__ */
876 #endif /* __CTYPE_HAS_8_BIT_LOCALES */
877 #ifdef __UCLIBC_HAS_WCHAR__
879 = (const unsigned char *) &__locale_mmap->tblwctype;
881 = (const unsigned char *) &__locale_mmap->tblwuplow;
883 = (const int16_t *) &__locale_mmap->tblwuplow_diff;
885 /* = (const unsigned char *) &__locale_mmap->tblwcomb; */
887 #endif /* __UCLIBC_HAS_WCHAR__ */
889 /* Initially, set things up to use the global C ctype tables.
890 * This is correct for C (ASCII) and UTF-8 based locales (except tr_TR). */
891 #ifdef __UCLIBC_HAS_XLOCALE__
892 base->__ctype_b = __C_ctype_b;
893 base->__ctype_tolower = __C_ctype_tolower;
894 base->__ctype_toupper = __C_ctype_toupper;
895 #else /* __UCLIBC_HAS_XLOCALE__ */
896 __ctype_b = __C_ctype_b;
897 __ctype_tolower = __C_ctype_tolower;
898 __ctype_toupper = __C_ctype_toupper;
899 #endif /* __UCLIBC_HAS_XLOCALE__ */
901 #ifdef __UCLIBC_MJN3_ONLY__
902 #warning TODO: Initialize code2flag correctly based on locale_mmap.
904 base->code2flag = __code2flag;
906 _locale_set_l((unsigned char*) C_LOCALE_SELECTOR, base);
909 void _locale_init(void)
911 /* TODO: mmap the locale file */
914 _locale_init_l(__global_locale);
918 /**********************************************************************/
919 #if defined(L_nl_langinfo) || defined(L_nl_langinfo_l)
921 #ifdef __LOCALE_C_ONLY
923 /* We need to index 320 bytes of data, so you might initially think we
924 * need to store the offsets in shorts. But since the offset of the
925 * 64th item is 182, we'll store "offset - 2*64" for all items >= 64
926 * and always calculate the data offset as "offset[i] + 2*(i & 64)".
927 * This allows us to pack the data offsets in an unsigned char while
928 * also avoiding an "if".
930 * Note: Category order is assumed to be:
931 * ctype, numeric, monetary, time, collate, messages, all
936 /* Combine the data to avoid size penalty for seperate char arrays when
937 * compiler aligns objects. The original code is left in as documentation. */
938 #define cat_start nl_data
939 #define C_locale_data (nl_data + C_LC_ALL + 1 + 90)
941 static const unsigned char nl_data[C_LC_ALL + 1 + 90 + 320] = {
942 /* static const char cat_start[LC_ALL + 1] = { */
943 '\x00', '\x0b', '\x0e', '\x24', '\x56', '\x56', '\x5a',
945 /* static const char item_offset[90] = { */
946 '\x00', '\x02', '\x04', '\x06', '\x08', '\x0a', '\x0c', '\x0e',
947 '\x10', '\x12', '\x14', '\x1a', '\x1b', '\x1b', '\x1b', '\x1b',
948 '\x1b', '\x1b', '\x1b', '\x1b', '\x1b', '\x1c', '\x1c', '\x1c',
949 '\x1c', '\x1c', '\x1c', '\x1c', '\x1c', '\x1c', '\x1c', '\x1c',
950 '\x1c', '\x1c', '\x1c', '\x1e', '\x20', '\x24', '\x28', '\x2c',
951 '\x30', '\x34', '\x38', '\x3c', '\x43', '\x4a', '\x52', '\x5c',
952 '\x65', '\x6c', '\x75', '\x79', '\x7d', '\x81', '\x85', '\x89',
953 '\x8d', '\x91', '\x95', '\x99', '\x9d', '\xa1', '\xa5', '\xad',
954 '\x36', '\x3c', '\x42', '\x46', '\x4b', '\x50', '\x57', '\x61',
955 '\x69', '\x72', '\x7b', '\x7e', '\x81', '\x96', '\x9f', '\xa8',
956 '\xb3', '\xb3', '\xb3', '\xb3', '\xb3', '\xb3', '\xb4', '\xba',
959 /* static const char C_locale_data[320] = { */
960 '0', '\x00', '1', '\x00', '2', '\x00', '3', '\x00',
961 '4', '\x00', '5', '\x00', '6', '\x00', '7', '\x00',
962 '8', '\x00', '9', '\x00', 'A', 'S', 'C', 'I',
963 'I', '\x00', '.', '\x00', '\x7f', '\x00', '-', '\x00',
964 'S', 'u', 'n', '\x00', 'M', 'o', 'n', '\x00',
965 'T', 'u', 'e', '\x00', 'W', 'e', 'd', '\x00',
966 'T', 'h', 'u', '\x00', 'F', 'r', 'i', '\x00',
967 'S', 'a', 't', '\x00', 'S', 'u', 'n', 'd',
968 'a', 'y', '\x00', 'M', 'o', 'n', 'd', 'a',
969 'y', '\x00', 'T', 'u', 'e', 's', 'd', 'a',
970 'y', '\x00', 'W', 'e', 'd', 'n', 'e', 's',
971 'd', 'a', 'y', '\x00', 'T', 'h', 'u', 'r',
972 's', 'd', 'a', 'y', '\x00', 'F', 'r', 'i',
973 'd', 'a', 'y', '\x00', 'S', 'a', 't', 'u',
974 'r', 'd', 'a', 'y', '\x00', 'J', 'a', 'n',
975 '\x00', 'F', 'e', 'b', '\x00', 'M', 'a', 'r',
976 '\x00', 'A', 'p', 'r', '\x00', 'M', 'a', 'y',
977 '\x00', 'J', 'u', 'n', '\x00', 'J', 'u', 'l',
978 '\x00', 'A', 'u', 'g', '\x00', 'S', 'e', 'p',
979 '\x00', 'O', 'c', 't', '\x00', 'N', 'o', 'v',
980 '\x00', 'D', 'e', 'c', '\x00', 'J', 'a', 'n',
981 'u', 'a', 'r', 'y', '\x00', 'F', 'e', 'b',
982 'r', 'u', 'a', 'r', 'y', '\x00', 'M', 'a',
983 'r', 'c', 'h', '\x00', 'A', 'p', 'r', 'i',
984 'l', '\x00', 'M', 'a', 'y', '\x00', 'J', 'u',
985 'n', 'e', '\x00', 'J', 'u', 'l', 'y', '\x00',
986 'A', 'u', 'g', 'u', 's', 't', '\x00', 'S',
987 'e', 'p', 't', 'e', 'm', 'b', 'e', 'r',
988 '\x00', 'O', 'c', 't', 'o', 'b', 'e', 'r',
989 '\x00', 'N', 'o', 'v', 'e', 'm', 'b', 'e',
990 'r', '\x00', 'D', 'e', 'c', 'e', 'm', 'b',
991 'e', 'r', '\x00', 'A', 'M', '\x00', 'P', 'M',
992 '\x00', '%', 'a', ' ', '%', 'b', ' ', '%',
993 'e', ' ', '%', 'H', ':', '%', 'M', ':',
994 '%', 'S', ' ', '%', 'Y', '\x00', '%', 'm',
995 '/', '%', 'd', '/', '%', 'y', '\x00', '%',
996 'H', ':', '%', 'M', ':', '%', 'S', '\x00',
997 '%', 'I', ':', '%', 'M', ':', '%', 'S',
998 ' ', '%', 'p', '\x00', '^', '[', 'y', 'Y',
999 ']', '\x00', '^', '[', 'n', 'N', ']', '\x00',
1002 char *nl_langinfo(nl_item item)
1007 if ((c = _NL_ITEM_CATEGORY(item)) < C_LC_ALL) {
1008 if ((i = cat_start[c] + _NL_ITEM_INDEX(item)) < cat_start[c+1]) {
1009 /* return (char *) C_locale_data + item_offset[i] + (i & 64); */
1010 return (char *) C_locale_data + nl_data[C_LC_ALL+1+i] + 2*(i & 64);
1013 return (char *) cat_start; /* Conveniently, this is the empty string. */
1015 libc_hidden_def(nl_langinfo)
1017 #else /* __LOCALE_C_ONLY */
1019 #if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE)
1023 char *nl_langinfo(nl_item item)
1025 return nl_langinfo_l(item, __UCLIBC_CURLOCALE);
1027 libc_hidden_def(nl_langinfo)
1029 #else /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
1031 libc_hidden_proto(__XL_NPP(nl_langinfo))
1033 static const char empty[] = "";
1035 char *__XL_NPP(nl_langinfo)(nl_item item __LOCALE_PARAM )
1037 unsigned int c = _NL_ITEM_CATEGORY(item);
1038 unsigned int i = _NL_ITEM_INDEX(item);
1040 if ((c < LC_ALL) && (i < __LOCALE_PTR->category_item_count[c])) {
1041 return ((char **)(((char *) __LOCALE_PTR)
1042 + __LOCALE_PTR->category_offsets[c]))[i];
1045 return (char *) empty;
1047 libc_hidden_def(__XL_NPP(nl_langinfo))
1049 #endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
1051 #endif /* __LOCALE_C_ONLY */
1054 /**********************************************************************/
1057 #warning mask defines for extra locale categories
1059 #ifdef __UCLIBC_MJN3_ONLY__
1060 #warning TODO: Move posix and utf8 strings.
1062 static const char posix[] = "POSIX";
1063 static const char utf8[] = "UTF-8";
1065 static int find_locale(int category_mask, const char *p,
1066 unsigned char *new_locale)
1069 const unsigned char *s;
1071 unsigned char lang_cult, codeset;
1073 #if defined(__LOCALE_DATA_AT_MODIFIERS_LENGTH) && 1
1074 /* Support standard locale handling for @-modifiers. */
1076 #ifdef __UCLIBC_MJN3_ONLY__
1077 #warning REMINDER: Fix buf size in find_locale.
1079 char buf[18]; /* TODO: 7+{max codeset name length} */
1082 if ((q = strchr(p,'@')) != NULL) {
1083 if ((((size_t)((q-p)-5)) > (sizeof(buf) - 5)) || (p[2] != '_')) {
1086 /* locale name at least 5 chars long and 3rd char is '_' */
1087 s = LOCALE_AT_MODIFIERS;
1089 if (!strcmp((char*) (s + 2), q + 1)) {
1092 s += 2 + *s; /* TODO - fix this throughout */
1097 assert(q - p < sizeof(buf));
1098 memcpy(buf, p, q-p);
1105 lang_cult = codeset = 0; /* Assume C and default codeset. */
1106 if (((*p == 'C') && !p[1]) || !strcmp(p, posix)) {
1110 if ((strlen(p) > 5) && (p[5] == '.')) { /* Codeset in locale name? */
1111 /* TODO: maybe CODESET_LIST + *s ??? */
1112 /* 7bit is 1, UTF-8 is 2, 8-bit is >= 3 */
1114 if (strcasecmp(utf8, p + 6) != 0) {/* TODO - fix! */
1117 ++codeset; /* Increment codeset first. */
1118 if (!strcmp((char*) CODESET_LIST + *s, p + 6)) {
1119 goto FIND_LANG_CULT;
1122 return 0; /* No matching codeset! */
1126 FIND_LANG_CULT: /* Find language_culture number. */
1128 do { /* TODO -- do a binary search? */
1129 /* TODO -- fix gen_mmap!*/
1130 ++lang_cult; /* Increment first since C/POSIX is 0. */
1131 if (!strncmp((char*) s, p, 5)) { /* Found a matching locale name; */
1135 } while (lang_cult < __LOCALE_DATA_NUM_LOCALE_NAMES);
1136 return 0; /* No matching language_culture! */
1138 FIND_LOCALE: /* Find locale row matching name and codeset */
1141 do { /* TODO -- do a binary search? */
1142 if ((lang_cult == *s) && ((codeset == s[1]) || (codeset == s[2]))) {
1146 if (category_mask & i) {
1147 /* Encode current locale row number. */
1148 ((unsigned char *) s)[0] = (n >> 7) | 0x80;
1149 ((unsigned char *) s)[1] = (n & 0x7f) | 0x80;
1153 } while (i < (1 << LC_ALL));
1155 return i; /* Return non-zero */
1157 s += __LOCALE_DATA_WIDTH_LOCALES;
1159 } while (n <= __LOCALE_DATA_NUM_LOCALES); /* We started at 1!!! */
1161 return 0; /* Unsupported locale. */
1164 static unsigned char *composite_locale(int category_mask, const char *locale,
1165 unsigned char *new_locale)
1167 char buf[MAX_LOCALE_STR];
1173 if (!strchr(locale,'=')) {
1174 if (!find_locale(category_mask, locale, new_locale)) {
1180 if (strlen(locale) >= sizeof(buf)) {
1183 stpcpy(buf, locale);
1186 t = strtok_r(buf, "=", &e); /* This can't fail because of strchr test above. */
1189 /* CATEGORY_NAMES is unsigned char* */
1190 while (strcmp((char*) CATEGORY_NAMES + (int) CATEGORY_NAMES[c], t)) {
1191 if (++c == LC_ALL) { /* Unknown category name! */
1195 t = strtok_r(NULL, ";", &e);
1197 if (component_mask & c) { /* Multiple components for one category. */
1200 component_mask |= c;
1201 if ((category_mask & c) && (!t || !find_locale(c, t, new_locale))) {
1204 } while ((t = strtok_r(NULL, "=", &e)) != NULL);
1206 if (category_mask & ~component_mask) { /* Category component(s) missing. */
1213 __locale_t newlocale(int category_mask, const char *locale, __locale_t base)
1217 unsigned char new_selector[LOCALE_SELECTOR_SIZE];
1219 if (category_mask == (1 << LC_ALL)) {
1220 category_mask = LC_ALL_MASK;
1223 if (!locale || ((unsigned)(category_mask) > LC_ALL_MASK)) {
1225 __set_errno(EINVAL);
1226 return NULL; /* No locale or illegal/unsupported category. */
1229 #ifdef __UCLIBC_MJN3_ONLY__
1230 #warning TODO: Rename cur_locale to locale_selector.
1232 strcpy((char *) new_selector,
1233 (base ? (char *) base->cur_locale : C_LOCALE_SELECTOR));
1235 if (!locale[0]) { /* locale == "", so check environment. */
1236 const char *envstr[4];
1238 envstr[0] = "LC_ALL";
1246 if (category_mask & i) {
1247 /* Note: SUSv3 doesn't define a fallback mechanism here.
1248 * So, if LC_ALL is invalid, we do _not_ continue trying
1249 * the other environment vars. */
1250 envstr[1] = (char*) CATEGORY_NAMES + CATEGORY_NAMES[k];
1255 break; /* now p == "POSIX" */
1261 /* The user set something... is it valid? */
1262 /* Note: Since we don't support user-supplied locales and
1263 * alternate paths, we don't need to worry about special
1264 * handling for suid/sgid apps. */
1265 if (!find_locale(i, p, new_selector)) {
1270 } while (++k < LC_ALL);
1271 } else if (!composite_locale(category_mask, locale, new_selector)) {
1275 #ifdef __UCLIBC_MJN3_ONLY__
1276 #warning TODO: Do a compatible codeset check!
1279 /* If we get here, the new selector corresponds to a valid locale. */
1281 #ifdef __UCLIBC_MJN3_ONLY__
1282 #warning CONSIDER: Probably want a _locale_new func to allow for caching of locales.
1286 _locale_set_l(new_selector, base);
1288 base = _locale_new(new_selector);
1292 base = calloc(1, sizeof(struct __uclibc_locale_struct));
1295 _locale_init_l(base);
1298 _locale_set_l(new_selector, base);
1303 #ifdef __UCLIBC_HAS_XLOCALE__
1304 libc_hidden_def(newlocale)
1308 /**********************************************************************/
1312 #ifdef __UCLIBC_MJN3_ONLY__
1313 #warning REMINDER: When we allocate ctype tables, remember to dup them.
1316 __locale_t duplocale(__locale_t dataset)
1322 assert(dataset != LC_GLOBAL_LOCALE);
1324 r = malloc(sizeof(struct __uclibc_locale_struct));
1326 n = 2 * dataset->collate.max_col_index + 2;
1327 i2w = calloc(n, sizeof(uint16_t));
1329 memcpy(r, dataset, sizeof(struct __uclibc_locale_struct));
1330 r->collate.index2weight = i2w;
1331 memcpy(i2w, dataset->collate.index2weight, n * sizeof(uint16_t));
1341 /**********************************************************************/
1344 #ifdef __UCLIBC_MJN3_ONLY__
1345 #warning REMINDER: When we allocate ctype tables, remember to free them.
1348 void freelocale(__locale_t dataset)
1350 assert(dataset != __global_locale);
1351 assert(dataset != LC_GLOBAL_LOCALE);
1353 free(dataset->collate.index2weight); /* Free collation data. */
1354 free(dataset); /* Free locale */
1358 /**********************************************************************/
1361 __locale_t uselocale(__locale_t dataset)
1366 old = __UCLIBC_CURLOCALE;
1368 if (dataset == LC_GLOBAL_LOCALE) {
1369 dataset = __global_locale;
1371 #ifdef __UCLIBC_HAS_THREADS__
1372 old = __curlocale_set(dataset);
1374 old = __curlocale_var;
1375 __curlocale_var = dataset;
1379 if (old == __global_locale) {
1380 return LC_GLOBAL_LOCALE;
1384 libc_hidden_def(uselocale)
1387 /**********************************************************************/
1388 #ifdef L___curlocale
1390 #ifdef __UCLIBC_HAS_THREADS__
1392 __locale_t weak_const_function __curlocale(void)
1394 return __curlocale_var; /* This is overriden by the thread version. */
1396 libc_hidden_weak(__curlocale)
1398 __locale_t weak_function __curlocale_set(__locale_t newloc)
1400 __locale_t oldloc = __curlocale_var;
1401 assert(newloc != LC_GLOBAL_LOCALE);
1402 __curlocale_var = newloc;
1405 libc_hidden_weak(__curlocale_set)
1410 /**********************************************************************/
1411 #ifdef L___locale_mbrtowc_l
1413 /* NOTE: This returns an int... not size_t. Also, it is not a general
1414 * routine. It is actually a very stripped-down version of mbrtowc
1415 * that takes a __locale_t arg. This is used by strcoll and strxfrm.
1416 * It is also used above to generate wchar_t versions of the decimal point
1417 * and thousands seperator. */
1420 #ifndef __CTYPE_HAS_UTF_8_LOCALES
1421 #warning __CTYPE_HAS_UTF_8_LOCALES not set!
1423 #ifndef __CTYPE_HAS_8_BIT_LOCALES
1424 #warning __CTYPE_HAS_8_BIT_LOCALES not set!
1427 #define Cc2wc_IDX_SHIFT __LOCALE_DATA_Cc2wc_IDX_SHIFT
1428 #define Cc2wc_ROW_LEN __LOCALE_DATA_Cc2wc_ROW_LEN
1430 extern size_t _wchar_utf8sntowcs(wchar_t *__restrict pwc, size_t wn,
1431 const char **__restrict src, size_t n,
1432 mbstate_t *ps, int allow_continuation) attribute_hidden;
1434 int attribute_hidden __locale_mbrtowc_l(wchar_t *__restrict dst,
1435 const char *__restrict src,
1438 #ifdef __CTYPE_HAS_UTF_8_LOCALES
1439 if (loc->encoding == __ctype_encoding_utf8) {
1441 const char *p = src;
1444 r = _wchar_utf8sntowcs(dst, 1, &p, SIZE_MAX, &ps, 1);
1445 return (r == 1) ? (p-src) : r; /* Need to return 0 if nul char. */
1449 #ifdef __CTYPE_HAS_8_BIT_LOCALES
1450 assert((loc->encoding == __ctype_encoding_7_bit) || (loc->encoding == __ctype_encoding_8_bit));
1452 assert(loc->encoding == __ctype_encoding_7_bit);
1455 if ((*dst = ((unsigned char)(*src))) < 0x80) { /* ASCII... */
1459 #ifdef __CTYPE_HAS_8_BIT_LOCALES
1460 if (loc->encoding == __ctype_encoding_8_bit) {
1461 wchar_t wc = *dst - 0x80;
1462 *dst = loc->tbl8c2wc[
1463 (loc->idx8c2wc[wc >> Cc2wc_IDX_SHIFT]
1464 << Cc2wc_IDX_SHIFT) + (wc & (Cc2wc_ROW_LEN - 1))];
1475 /**********************************************************************/