blob: e8fddf67c79741464c12ab7d9c0d31306006eb56 [file] [log] [blame]
lh9ed821d2023-04-07 01:36:19 -07001/* Copyright (C) 2002 Manuel Novoa III
2 *
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.
7 *
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.
12 *
13 * You should have received a copy of the GNU Library General Public
14 * License along with this library; if not, write to the Free
15 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 */
17
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.
24 *
25 * Dec 20, 2002
26 * Added in collation support and updated stub nl_langinfo.
27 *
28 * Aug 1, 2003
29 * Added glibc-like extended locale stuff (newlocale, duplocale, etc).
30 *
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.
35 *
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++.
40 */
41
42
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).
47 */
48
49#define __CTYPE_HAS_8_BIT_LOCALES 1
50
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>
60
61#ifdef __UCLIBC_MJN3_ONLY__
62#ifdef L_setlocale
63#warning TODO: Make the link_warning()s a config option?
64#endif
65#endif
66#undef link_warning
67#define link_warning(A,B)
68
69#undef __LOCALE_C_ONLY
70#ifndef __UCLIBC_HAS_LOCALE__
71#define __LOCALE_C_ONLY
72#endif /* __UCLIBC_HAS_LOCALE__ */
73
74
75#ifdef __LOCALE_C_ONLY
76
77#include <locale.h>
78
79#else /* __LOCALE_C_ONLY */
80
81#ifdef __UCLIBC_MJN3_ONLY__
82#ifdef L_setlocale
83#warning TODO: Fix the __CTYPE_HAS_8_BIT_LOCALES define at the top of the file.
84#warning TODO: Fix __WCHAR_ENABLED.
85#endif
86#endif
87
88/* Need to include this before locale.h and xlocale.h! */
89#include <bits/uClibc_locale.h>
90
91#undef CODESET_LIST
92#define CODESET_LIST (__locale_mmap->codeset_list)
93
94#ifdef __UCLIBC_HAS_XLOCALE__
95#include <xlocale.h>
96#include <locale.h>
97#else /* __UCLIBC_HAS_XLOCALE__ */
98/* We need this internally... */
99#define __UCLIBC_HAS_XLOCALE__ 1
100#include <xlocale.h>
101#include <locale.h>
102#undef __UCLIBC_HAS_XLOCALE__
103#endif /* __UCLIBC_HAS_XLOCALE__ */
104
105#include <wchar.h>
106
107#define LOCALE_NAMES (__locale_mmap->locale_names5)
108#define LOCALES (__locale_mmap->locales)
109#define LOCALE_AT_MODIFIERS (__locale_mmap->locale_at_modifiers)
110#define CATEGORY_NAMES (__locale_mmap->lc_names)
111
112#ifdef __UCLIBC_MJN3_ONLY__
113#warning REMINDER: redo the MAX_LOCALE_STR stuff...
114#endif
115#define MAX_LOCALE_STR 256 /* TODO: Only sufficient for current case. */
116#define MAX_LOCALE_CATEGORY_STR 32 /* TODO: Only sufficient for current case. */
117/* Note: Best if MAX_LOCALE_CATEGORY_STR is a power of 2. */
118
119extern int _locale_set_l(const unsigned char *p, __locale_t base) attribute_hidden;
120extern void _locale_init_l(__locale_t base) attribute_hidden;
121
122#endif /* __LOCALE_C_ONLY */
123
124#undef LOCALE_STRING_SIZE
125#define LOCALE_SELECTOR_SIZE (2 * __LC_ALL + 2)
126
127#ifdef __UCLIBC_MJN3_ONLY__
128#ifdef L_setlocale
129#warning TODO: Create a C locale selector string.
130#endif
131#endif
132#define C_LOCALE_SELECTOR "\x23\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80"
133
134
135#include <langinfo.h>
136#include <nl_types.h>
137
138/**********************************************************************/
139#ifdef L_setlocale
140
141#ifdef __LOCALE_C_ONLY
142
143link_warning(setlocale,"REMINDER: The 'setlocale' function supports only C|POSIX locales.")
144
145static const char C_string[] = "C";
146
147char *setlocale(int category, register const char *locale)
148{
149 return ( (((unsigned int)(category)) <= LC_ALL)
150 && ( (!locale) /* Request for locale category string. */
151 || (!*locale) /* Implementation-defined default is C. */
152 || ((*locale == 'C') && !locale[1])
153 || (!strcmp(locale, "POSIX"))) )
154 ? (char *) C_string /* Always in C/POSIX locale. */
155 : NULL;
156}
157
158#else /* ---------------------------------------------- __LOCALE_C_ONLY */
159
160#ifdef __UCLIBC_HAS_THREADS__
161link_warning(setlocale,"REMINDER: The 'setlocale' function is _not_ threadsafe except for simple queries.")
162#endif
163
164#if !defined(__LOCALE_DATA_NUM_LOCALES) || (__LOCALE_DATA_NUM_LOCALES <= 1)
165#error locales enabled, but not data other than for C locale!
166#endif
167
168#ifdef __UCLIBC_MJN3_ONLY__
169#warning TODO: Move posix and utf8 strings.
170#endif
171static const char posix[] = "POSIX";
172static const char utf8[] = "UTF-8";
173
174#ifdef __UCLIBC_MJN3_ONLY__
175#warning TODO: Fix dimensions of hr_locale.
176#endif
177/* Individual category strings start at hr_locale + category * MAX_LOCALE_CATEGORY.
178 * This holds for LC_ALL as well.
179 */
180static char hr_locale[(MAX_LOCALE_CATEGORY_STR * LC_ALL) + MAX_LOCALE_STR];
181
182
183static void update_hr_locale(const unsigned char *spec)
184{
185 const unsigned char *loc;
186 const unsigned char *s;
187 char *n;
188 int i, category, done;
189
190 done = category = 0;
191 do {
192 s = spec + 1;
193 n = hr_locale + category * MAX_LOCALE_CATEGORY_STR;
194
195 if (category == LC_ALL) {
196 done = 1;
197 for (i = 0 ; i < LC_ALL-1 ; i += 2) {
198 if ((s[i] != s[i+2]) || (s[i+1] != s[i+3])) {
199 goto SKIP;
200 }
201 }
202 /* All categories the same, so simplify string by using a single
203 * category. */
204 category = LC_CTYPE;
205 }
206
207 SKIP:
208 i = (category == LC_ALL) ? 0 : category;
209 s += 2*i;
210
211 do {
212 if ((*s != 0xff) || (s[1] != 0xff)) {
213 loc = LOCALES
214 + __LOCALE_DATA_WIDTH_LOCALES * ((((int)(*s & 0x7f)) << 7)
215 + (s[1] & 0x7f));
216 if (category == LC_ALL) {
217 /* CATEGORY_NAMES is unsigned char* */
218 n = stpcpy(n, (char*) CATEGORY_NAMES + (int) CATEGORY_NAMES[i]);
219 *n++ = '=';
220 }
221 if (*loc == 0) {
222 *n++ = 'C';
223 *n = 0;
224 } else {
225 char at = 0;
226 memcpy(n, LOCALE_NAMES + 5*((*loc)-1), 5);
227 if (n[2] != '_') {
228 at = n[2];
229 n[2] = '_';
230 }
231 n += 5;
232 *n++ = '.';
233 if (loc[2] == 2) {
234 n = stpcpy(n, utf8);
235 } else if (loc[2] >= 3) {
236 n = stpcpy(n, (char*) CODESET_LIST + (int)(CODESET_LIST[loc[2] - 3]));
237 }
238 if (at) {
239 const char *q;
240 *n++ = '@';
241 q = (char*) LOCALE_AT_MODIFIERS;
242 do {
243 if (q[1] == at) {
244 n = stpcpy(n, q+2);
245 break;
246 }
247 q += 2 + *q;
248 } while (*q);
249 }
250 }
251 *n++ = ';';
252 }
253 s += 2;
254 } while (++i < category);
255 *--n = 0; /* Remove trailing ';' and nul-terminate. */
256
257 ++category;
258 } while (!done);
259}
260
261char *setlocale(int category, const char *locale)
262{
263 if (((unsigned int)(category)) > LC_ALL) {
264#if 0
265 __set_errno(EINVAL); /* glibc sets errno -- SUSv3 doesn't say. */
266#endif
267 return NULL; /* Illegal/unsupported category. */
268 }
269
270 if (locale != NULL) { /* Not just a query... */
271 if (!newlocale((1 << category), locale, __global_locale)) {
272 return NULL; /* Failed! */
273 }
274 update_hr_locale(__global_locale->cur_locale);
275 }
276
277 /* Either a query or a successful set, so return current locale string. */
278 return hr_locale + (category * MAX_LOCALE_CATEGORY_STR);
279}
280
281#endif /* __LOCALE_C_ONLY */
282
283#endif
284/**********************************************************************/
285#ifdef L_localeconv
286
287/* Note: We assume here that the compiler does the sane thing regarding
288 * placement of the fields in the struct. If necessary, we could ensure
289 * this usings an array of offsets but at some size cost. */
290
291
292#ifdef __LOCALE_C_ONLY
293
294link_warning(localeconv,"REMINDER: The 'localeconv' function is hardwired for C/POSIX locale only.")
295
296static struct lconv the_lconv;
297
298static const char decpt[] = ".";
299
300struct lconv *localeconv(void)
301{
302 register char *p = (char *)(&the_lconv);
303
304 *((char **)p) = (char *) decpt;
305 do {
306 p += sizeof(char **);
307 *((char **)p) = (char *) (decpt+1);
308 } while (p < (char *) &the_lconv.negative_sign);
309
310 p = (&the_lconv.int_frac_digits);
311 do {
312 *p = CHAR_MAX;
313 ++p;
314 } while (p <= &the_lconv.int_n_sign_posn);
315
316 return &the_lconv;
317}
318
319#else /* __LOCALE_C_ONLY */
320
321static struct lconv the_lconv;
322
323struct lconv *localeconv(void)
324{
325 register char *p = (char *) &the_lconv;
326 register char **q = (char **) &(__UCLIBC_CURLOCALE->decimal_point);
327
328 do {
329 *((char **)p) = *q;
330 p += sizeof(char **);
331 ++q;
332 } while (p < &the_lconv.int_frac_digits);
333
334 do {
335 *p = **q;
336 ++p;
337 ++q;
338 } while (p <= &the_lconv.int_n_sign_posn);
339
340 return &the_lconv;
341}
342
343#endif /* __LOCALE_C_ONLY */
344
345libc_hidden_def(localeconv)
346
347#endif
348/**********************************************************************/
349#if defined(L__locale_init) && !defined(__LOCALE_C_ONLY)
350
351struct __uclibc_locale_struct __global_locale_data;
352
353__locale_t __global_locale = &__global_locale_data;
354
355#ifdef __UCLIBC_HAS_XLOCALE__
356__locale_t __curlocale_var = &__global_locale_data;
357#endif
358
359/*----------------------------------------------------------------------*/
360#ifdef __UCLIBC_MJN3_ONLY__
361#warning TODO: Move utf8 and ascii strings.
362#endif
363static const char utf8[] = "UTF-8";
364static const char ascii[] = "ASCII";
365
366typedef struct {
367 uint16_t num_base;
368 uint16_t num_der;
369 uint16_t MAX_WEIGHTS;
370 uint16_t num_index2weight;
371#define num_index2ruleidx num_index2weight
372 uint16_t num_weightstr;
373 uint16_t num_multistart;
374 uint16_t num_override;
375 uint16_t num_ruletable;
376} coldata_header_t;
377
378typedef struct {
379 uint16_t num_weights;
380 uint16_t num_starters;
381 uint16_t ii_shift;
382 uint16_t ti_shift;
383 uint16_t ii_len;
384 uint16_t ti_len;
385 uint16_t max_weight;
386 uint16_t num_col_base;
387 uint16_t max_col_index;
388 uint16_t undefined_idx;
389 uint16_t range_low;
390 uint16_t range_count;
391 uint16_t range_base_weight;
392 uint16_t range_rule_offset;
393
394 uint16_t index2weight_offset;
395 uint16_t index2ruleidx_offset;
396 uint16_t multistart_offset;
397 uint16_t wcs2colidt_offset_low;
398 uint16_t wcs2colidt_offset_hi;
399} coldata_base_t;
400
401typedef struct {
402 uint16_t base_idx;
403 uint16_t undefined_idx;
404 uint16_t overrides_offset;
405 uint16_t multistart_offset;
406} coldata_der_t;
407
408static int init_cur_collate(int der_num, __collate_t *cur_collate)
409{
410 const uint16_t *__locale_collate_tbl = __locale_mmap->collate_data;
411 coldata_header_t *cdh;
412 coldata_base_t *cdb;
413 coldata_der_t *cdd;
414 const uint16_t *p;
415 size_t n;
416 uint16_t i, w;
417
418#ifdef __UCLIBC_MJN3_ONLY__
419#warning kill of x86-specific asserts
420#endif
421#if 0
422 assert(sizeof(coldata_base_t) == 19*2);
423 assert(sizeof(coldata_der_t) == 4*2);
424 assert(sizeof(coldata_header_t) == 8*2);
425#endif
426
427 if (!der_num) { /* C locale... special */
428 cur_collate->num_weights = 0;
429 return 1;
430 }
431
432 --der_num;
433
434 cdh = (coldata_header_t *) __locale_collate_tbl;
435
436#ifdef __UCLIBC_MJN3_ONLY__
437#warning CONSIDER: Should we assert here?
438#endif
439#if 0
440 if (der_num >= cdh->num_der) {
441 return 0;
442 }
443#else
444 assert((der_num < cdh->num_der));
445#endif
446
447 cdd = (coldata_der_t *)(__locale_collate_tbl
448 + (sizeof(coldata_header_t)
449 + cdh->num_base * sizeof(coldata_base_t)
450 + der_num * sizeof(coldata_der_t)
451 )/2 );
452
453 cdb = (coldata_base_t *)(__locale_collate_tbl
454 + (sizeof(coldata_header_t)
455 + cdd->base_idx * sizeof(coldata_base_t)
456 )/2 );
457
458 memcpy(cur_collate, cdb, offsetof(coldata_base_t,index2weight_offset));
459 cur_collate->undefined_idx = cdd->undefined_idx;
460
461 cur_collate->ti_mask = (1 << cur_collate->ti_shift)-1;
462 cur_collate->ii_mask = (1 << cur_collate->ii_shift)-1;
463
464/* fflush(stdout); */
465/* fprintf(stderr,"base=%d num_col_base: %d %d\n", cdd->base_idx ,cur_collate->num_col_base, cdb->num_col_base); */
466
467 n = (sizeof(coldata_header_t) + cdh->num_base * sizeof(coldata_base_t)
468 + cdh->num_der * sizeof(coldata_der_t))/2;
469
470/* fprintf(stderr,"n = %d\n", n); */
471 cur_collate->index2weight_tbl = __locale_collate_tbl + n + cdb->index2weight_offset;
472/* fprintf(stderr,"i2w = %d\n", n + cdb->index2weight_offset); */
473 n += cdh->num_index2weight;
474 cur_collate->index2ruleidx_tbl = __locale_collate_tbl + n + cdb->index2ruleidx_offset;
475/* fprintf(stderr,"i2r = %d\n", n + cdb->index2ruleidx_offset); */
476 n += cdh->num_index2ruleidx;
477 cur_collate->multistart_tbl = __locale_collate_tbl + n + cdd->multistart_offset;
478/* fprintf(stderr,"mts = %d\n", n + cdb->multistart_offset); */
479 n += cdh->num_multistart;
480 cur_collate->overrides_tbl = __locale_collate_tbl + n + cdd->overrides_offset;
481/* fprintf(stderr,"ovr = %d\n", n + cdd->overrides_offset); */
482 n += cdh->num_override;
483 cur_collate->ruletable = __locale_collate_tbl + n;
484/* fprintf(stderr, "rtb = %d\n", n); */
485 n += cdh->num_ruletable;
486 cur_collate->weightstr = __locale_collate_tbl + n;
487/* fprintf(stderr,"wts = %d\n", n); */
488 n += cdh->num_weightstr;
489 cur_collate->wcs2colidt_tbl = __locale_collate_tbl + n
490 + (((unsigned long)(cdb->wcs2colidt_offset_hi)) << 16)
491 + cdb->wcs2colidt_offset_low;
492/* fprintf(stderr,"wcs = %lu\n", n + (((unsigned long)(cdb->wcs2colidt_offset_hi)) << 16) */
493/* + cdb->wcs2colidt_offset_low); */
494
495 cur_collate->MAX_WEIGHTS = cdh->MAX_WEIGHTS;
496
497#ifdef __UCLIBC_MJN3_ONLY__
498#warning CONSIDER: Fix the +1 by increasing max_col_index?
499#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#endif
501
502 cur_collate->index2weight = calloc(2*cur_collate->max_col_index+2,
503 sizeof(uint16_t));
504 if (!cur_collate->index2weight) {
505 return 0;
506 }
507 cur_collate->index2ruleidx = cur_collate->index2weight
508 + cur_collate->max_col_index + 1;
509
510 memcpy(cur_collate->index2weight, cur_collate->index2weight_tbl,
511 cur_collate->num_col_base * sizeof(uint16_t));
512 memcpy(cur_collate->index2ruleidx, cur_collate->index2ruleidx_tbl,
513 cur_collate->num_col_base * sizeof(uint16_t));
514
515 /* now do the overrides */
516 p = cur_collate->overrides_tbl;
517 while (*p > 1) {
518/* fprintf(stderr, "processing override -- count = %d\n", *p); */
519 n = *p++;
520 w = *p++;
521 do {
522 i = *p++;
523/* fprintf(stderr, " i=%d (%#x) w=%d *p=%d\n", i, i, w, *p); */
524 cur_collate->index2weight[i-1] = w++;
525 cur_collate->index2ruleidx[i-1] = *p++;
526 } while (--n);
527 }
528 assert(*p == 1);
529 while (*++p) {
530 i = *p;
531/* fprintf(stderr, " i=%d (%#x) w=%d *p=%d\n", i, i, p[1], p[2]); */
532 cur_collate->index2weight[i-1] = *++p;
533 cur_collate->index2ruleidx[i-1] = *++p;
534 }
535
536
537 for (i=0 ; i < cur_collate->multistart_tbl[0] ; i++) {
538 p = cur_collate->multistart_tbl;
539/* fprintf(stderr, "%2d of %2d: %d ", i, cur_collate->multistart_tbl[0], p[i]); */
540 p += p[i];
541
542 do {
543 n = *p++;
544 do {
545 if (!*p) { /* found it */
546/* fprintf(stderr, "found: n=%d (%#lx) |%.*ls|\n", n, (int) *cs->s, n, cs->s); */
547/* fprintf(stderr, ": %d - single\n", n); */
548 goto FOUND;
549 }
550 /* the lookup check here is safe since we're assured that *p is a valid colidex */
551/* fprintf(stderr, "lookup(%lc)==%d *p==%d\n", cs->s[n], lookup(cs->s[n]), (int) *p); */
552/* fprintf(stderr, ": %d - ", n); */
553 do {
554/* fprintf(stderr, "%d|", *p); */
555 } while (*p++);
556 break;
557 } while (1);
558 } while (1);
559 FOUND:
560 continue;
561 }
562
563 return 1;
564}
565
566int attribute_hidden _locale_set_l(const unsigned char *p, __locale_t base)
567{
568 const char **x;
569 unsigned char *s = base->cur_locale + 1;
570 const size_t *stp;
571 const unsigned char *r;
572 const uint16_t *io;
573 const uint16_t *ii;
574 const unsigned char *d;
575 int row; /* locale row */
576 int crow; /* category row */
577 int len;
578 int c;
579 int i = 0;
580 __collate_t newcol;
581
582 ++p;
583
584 newcol.index2weight = NULL;
585 if ((p[2*LC_COLLATE] != s[2*LC_COLLATE])
586 || (p[2*LC_COLLATE + 1] != s[2*LC_COLLATE + 1])
587 ) {
588 row = (((int)(*p & 0x7f)) << 7) + (p[1] & 0x7f);
589 assert(row < __LOCALE_DATA_NUM_LOCALES);
590 if (!init_cur_collate(__locale_mmap->locales[ __LOCALE_DATA_WIDTH_LOCALES
591 * row + 3 + LC_COLLATE ],
592 &newcol)
593 ) {
594 return 0; /* calloc failed. */
595 }
596 free(base->collate.index2weight);
597 memcpy(&base->collate, &newcol, sizeof(__collate_t));
598 }
599
600 do {
601 if ((*p != *s) || (p[1] != s[1])) {
602 row = (((int)(*p & 0x7f)) << 7) + (p[1] & 0x7f);
603 assert(row < __LOCALE_DATA_NUM_LOCALES);
604
605 *s = *p;
606 s[1] = p[1];
607
608 if ((i != LC_COLLATE)
609 && ((len = __locale_mmap->lc_common_item_offsets_LEN[i]) != 0)
610 ) {
611 crow = __locale_mmap->locales[ __LOCALE_DATA_WIDTH_LOCALES * row
612 + 3 + i ]
613 * len;
614
615 x = (const char **)(((char *) base)
616 + base->category_offsets[i]);
617
618 stp = __locale_mmap->lc_common_tbl_offsets + 4*i;
619 r = (const unsigned char *)( ((char *)__locale_mmap) + *stp );
620 io = (const uint16_t *)( ((char *)__locale_mmap) + *++stp );
621 ii = (const uint16_t *)( ((char *)__locale_mmap) + *++stp );
622 d = (const unsigned char *)( ((char *)__locale_mmap) + *++stp );
623 for (c = 0; c < len; c++) {
624 x[c] = (char*)(d + ii[r[crow + c] + io[c]]);
625 }
626 }
627 if (i == LC_CTYPE) {
628 c = __locale_mmap->locales[ __LOCALE_DATA_WIDTH_LOCALES * row
629 + 2 ]; /* codeset */
630 if (c <= 2) {
631 if (c == 2) {
632 base->codeset = utf8;
633 base->encoding = __ctype_encoding_utf8;
634 /* TODO - fix for bcc */
635 base->mb_cur_max = 6;
636 } else {
637 assert(c == 1);
638 base->codeset = ascii;
639 base->encoding = __ctype_encoding_7_bit;
640 base->mb_cur_max = 1;
641 }
642 } else {
643 const __codeset_8_bit_t *c8b;
644 r = CODESET_LIST;
645 c -= 3;
646 base->codeset = (char *) (r + r[c]);
647 base->encoding = __ctype_encoding_8_bit;
648#ifdef __UCLIBC_MJN3_ONLY__
649#warning REMINDER: update 8 bit mb_cur_max when translit implemented!
650#endif
651 /* TODO - update when translit implemented! */
652 base->mb_cur_max = 1;
653 c8b = __locale_mmap->codeset_8_bit + c;
654#ifdef __CTYPE_HAS_8_BIT_LOCALES
655 base->idx8ctype = c8b->idx8ctype;
656 base->idx8uplow = c8b->idx8uplow;
657#ifdef __UCLIBC_HAS_WCHAR__
658 base->idx8c2wc = c8b->idx8c2wc;
659 base->idx8wc2c = c8b->idx8wc2c;
660 /* translit */
661#endif /* __UCLIBC_HAS_WCHAR__ */
662
663 /* What follows is fairly bloated, but it is just a hack
664 * to get the 8-bit codeset ctype stuff functioning.
665 * All of this will be replaced in the next generation
666 * of locale support anyway... */
667
668 memcpy(base->__ctype_b_data,
669 __C_ctype_b - __UCLIBC_CTYPE_B_TBL_OFFSET,
670 (256 + __UCLIBC_CTYPE_B_TBL_OFFSET)
671 * sizeof(__ctype_mask_t));
672 memcpy(base->__ctype_tolower_data,
673 __C_ctype_tolower - __UCLIBC_CTYPE_TO_TBL_OFFSET,
674 (256 + __UCLIBC_CTYPE_TO_TBL_OFFSET)
675 * sizeof(__ctype_touplow_t));
676 memcpy(base->__ctype_toupper_data,
677 __C_ctype_toupper - __UCLIBC_CTYPE_TO_TBL_OFFSET,
678 (256 + __UCLIBC_CTYPE_TO_TBL_OFFSET)
679 * sizeof(__ctype_touplow_t));
680
681#define Cctype_TBL_MASK ((1 << __LOCALE_DATA_Cctype_IDX_SHIFT) - 1)
682#define Cctype_IDX_OFFSET (128 >> __LOCALE_DATA_Cctype_IDX_SHIFT)
683
684 {
685 int u;
686 __ctype_mask_t m;
687
688 for (u=0 ; u < 128 ; u++) {
689#ifdef __LOCALE_DATA_Cctype_PACKED
690 c = base->tbl8ctype
691 [ ((int)(c8b->idx8ctype
692 [(u >> __LOCALE_DATA_Cctype_IDX_SHIFT) ])
693 << (__LOCALE_DATA_Cctype_IDX_SHIFT - 1))
694 + ((u & Cctype_TBL_MASK) >> 1)];
695 c = (u & 1) ? (c >> 4) : (c & 0xf);
696#else
697 c = base->tbl8ctype
698 [ ((int)(c8b->idx8ctype
699 [(u >> __LOCALE_DATA_Cctype_IDX_SHIFT) ])
700 << __LOCALE_DATA_Cctype_IDX_SHIFT)
701 + (u & Cctype_TBL_MASK) ];
702#endif
703
704 m = base->code2flag[c];
705
706 base->__ctype_b_data
707 [128 + __UCLIBC_CTYPE_B_TBL_OFFSET + u]
708 = m;
709
710#ifdef __UCLIBC_HAS_CTYPE_SIGNED__
711 if (((signed char)(128 + u)) != -1) {
712 base->__ctype_b_data[__UCLIBC_CTYPE_B_TBL_OFFSET
713 + ((signed char)(128 + u))]
714 = m;
715 }
716#endif
717
718 base->__ctype_tolower_data
719 [128 + __UCLIBC_CTYPE_TO_TBL_OFFSET + u]
720 = 128 + u;
721 base->__ctype_toupper_data
722 [128 + __UCLIBC_CTYPE_TO_TBL_OFFSET + u]
723 = 128 + u;
724
725 if (m & (_ISlower|_ISupper)) {
726 c = base->tbl8uplow
727 [ ((int)(c8b->idx8uplow
728 [u >> __LOCALE_DATA_Cuplow_IDX_SHIFT])
729 << __LOCALE_DATA_Cuplow_IDX_SHIFT)
730 + ((128 + u)
731 & ((1 << __LOCALE_DATA_Cuplow_IDX_SHIFT)
732 - 1)) ];
733 if (m & _ISlower) {
734 base->__ctype_toupper_data
735 [128 + __UCLIBC_CTYPE_TO_TBL_OFFSET + u]
736 = (unsigned char)(128 + u + c);
737#ifdef __UCLIBC_HAS_CTYPE_SIGNED__
738 if (((signed char)(128 + u)) != -1) {
739 base->__ctype_toupper_data
740 [__UCLIBC_CTYPE_TO_TBL_OFFSET
741 + ((signed char)(128 + u))]
742 = (unsigned char)(128 + u + c);
743 }
744#endif
745 } else {
746 base->__ctype_tolower_data
747 [128 + __UCLIBC_CTYPE_TO_TBL_OFFSET + u]
748 = (unsigned char)(128 + u - c);
749#ifdef __UCLIBC_HAS_CTYPE_SIGNED__
750 if (((signed char)(128 + u)) != -1) {
751 base->__ctype_tolower_data
752 [__UCLIBC_CTYPE_TO_TBL_OFFSET
753 + ((signed char)(128 + u))]
754 = (unsigned char)(128 + u - c);
755 }
756#endif
757 }
758 }
759 }
760 }
761
762#ifdef __UCLIBC_HAS_XLOCALE__
763 base->__ctype_b = base->__ctype_b_data
764 + __UCLIBC_CTYPE_B_TBL_OFFSET;
765 base->__ctype_tolower = base->__ctype_tolower_data
766 + __UCLIBC_CTYPE_TO_TBL_OFFSET;
767 base->__ctype_toupper = base->__ctype_toupper_data
768 + __UCLIBC_CTYPE_TO_TBL_OFFSET;
769#else /* __UCLIBC_HAS_XLOCALE__ */
770 __ctype_b = base->__ctype_b_data
771 + __UCLIBC_CTYPE_B_TBL_OFFSET;
772 __ctype_tolower = base->__ctype_tolower_data
773 + __UCLIBC_CTYPE_TO_TBL_OFFSET;
774 __ctype_toupper = base->__ctype_toupper_data
775 + __UCLIBC_CTYPE_TO_TBL_OFFSET;
776#endif /* __UCLIBC_HAS_XLOCALE__ */
777
778#endif /* __CTYPE_HAS_8_BIT_LOCALES */
779 }
780#ifdef __UCLIBC_MJN3_ONLY__
781#warning TODO: Put the outdigit string length in the locale_mmap object.
782#endif
783 d = base->outdigit_length;
784 x = &base->outdigit0_mb;
785 for (c = 0 ; c < 10 ; c++) {
786 ((unsigned char *)d)[c] = strlen(x[c]);
787 assert(d[c] > 0);
788 }
789 } else if (i == LC_NUMERIC) {
790 assert(LC_NUMERIC > LC_CTYPE); /* Need ctype initialized. */
791
792 base->decimal_point_len
793 = __locale_mbrtowc_l(&base->decimal_point_wc,
794 base->decimal_point, base);
795 assert(base->decimal_point_len > 0);
796 assert(base->decimal_point[base->decimal_point_len] == 0);
797
798 if (*base->grouping) {
799 base->thousands_sep_len
800 = __locale_mbrtowc_l(&base->thousands_sep_wc,
801 base->thousands_sep, base);
802#if 1
803#ifdef __UCLIBC_MJN3_ONLY__
804#warning TODO: Remove hack involving grouping without a thousep char (bg_BG).
805#endif
806 assert(base->thousands_sep_len >= 0);
807 if (base->thousands_sep_len == 0) {
808 base->grouping = base->thousands_sep; /* empty string */
809 }
810 assert(base->thousands_sep[base->thousands_sep_len] == 0);
811#else
812 assert(base->thousands_sep_len > 0);
813 assert(base->thousands_sep[base->thousands_sep_len] == 0);
814#endif
815 }
816
817/* } else if (i == LC_COLLATE) { */
818/* init_cur_collate(__locale_mmap->locales[ __LOCALE_DATA_WIDTH_LOCALES */
819/* * row + 3 + i ], */
820/* &base->collate); */
821 }
822 }
823 ++i;
824 p += 2;
825 s += 2;
826 } while (i < LC_ALL);
827
828 return 1;
829}
830
831static const uint16_t __code2flag[16] = {
832 0, /* unclassified = 0 */
833 _ISprint|_ISgraph|_ISalnum|_ISalpha, /* alpha_nonupper_nonlower */
834 _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, /* alpha_lower */
835 _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower|_ISupper, /* alpha_upper_lower */
836 _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, /* alpha_upper */
837 _ISprint|_ISgraph|_ISalnum|_ISdigit, /* digit */
838 _ISprint|_ISgraph|_ISpunct, /* punct */
839 _ISprint|_ISgraph, /* graph */
840 _ISprint|_ISspace, /* print_space_nonblank */
841 _ISprint|_ISspace|_ISblank, /* print_space_blank */
842 _ISspace, /* space_nonblank_noncntrl */
843 _ISspace|_ISblank, /* space_blank_noncntrl */
844 _IScntrl|_ISspace, /* cntrl_space_nonblank */
845 _IScntrl|_ISspace|_ISblank, /* cntrl_space_blank */
846 _IScntrl /* cntrl_nonspace */
847};
848
849void attribute_hidden _locale_init_l(__locale_t base)
850{
851 memset(base->cur_locale, 0, LOCALE_SELECTOR_SIZE);
852 base->cur_locale[0] = '#';
853
854 memcpy(base->category_item_count,
855 __locale_mmap->lc_common_item_offsets_LEN,
856 LC_ALL);
857
858 ++base->category_item_count[0]; /* Increment for codeset entry. */
859 base->category_offsets[0] = offsetof(struct __uclibc_locale_struct, outdigit0_mb);
860 base->category_offsets[1] = offsetof(struct __uclibc_locale_struct, decimal_point);
861 base->category_offsets[2] = offsetof(struct __uclibc_locale_struct, int_curr_symbol);
862 base->category_offsets[3] = offsetof(struct __uclibc_locale_struct, abday_1);
863/* base->category_offsets[4] = offsetof(struct __uclibc_locale_struct, collate???); */
864 base->category_offsets[5] = offsetof(struct __uclibc_locale_struct, yesexpr);
865
866#ifdef __CTYPE_HAS_8_BIT_LOCALES
867 base->tbl8ctype
868 = (const unsigned char *) &__locale_mmap->tbl8ctype;
869 base->tbl8uplow
870 = (const unsigned char *) &__locale_mmap->tbl8uplow;
871#ifdef __UCLIBC_HAS_WCHAR__
872 base->tbl8c2wc
873 = (const uint16_t *) &__locale_mmap->tbl8c2wc;
874 base->tbl8wc2c
875 = (const unsigned char *) &__locale_mmap->tbl8wc2c;
876 /* translit */
877#endif /* __UCLIBC_HAS_WCHAR__ */
878#endif /* __CTYPE_HAS_8_BIT_LOCALES */
879#ifdef __UCLIBC_HAS_WCHAR__
880 base->tblwctype
881 = (const unsigned char *) &__locale_mmap->tblwctype;
882 base->tblwuplow
883 = (const unsigned char *) &__locale_mmap->tblwuplow;
884 base->tblwuplow_diff
885 = (const int16_t *) &__locale_mmap->tblwuplow_diff;
886/* base->tblwcomb */
887/* = (const unsigned char *) &__locale_mmap->tblwcomb; */
888 /* width?? */
889#endif /* __UCLIBC_HAS_WCHAR__ */
890
891 /* Initially, set things up to use the global C ctype tables.
892 * This is correct for C (ASCII) and UTF-8 based locales (except tr_TR). */
893#ifdef __UCLIBC_HAS_XLOCALE__
894 base->__ctype_b = __C_ctype_b;
895 base->__ctype_tolower = __C_ctype_tolower;
896 base->__ctype_toupper = __C_ctype_toupper;
897#else /* __UCLIBC_HAS_XLOCALE__ */
898 __ctype_b = __C_ctype_b;
899 __ctype_tolower = __C_ctype_tolower;
900 __ctype_toupper = __C_ctype_toupper;
901#endif /* __UCLIBC_HAS_XLOCALE__ */
902
903#ifdef __UCLIBC_MJN3_ONLY__
904#warning TODO: Initialize code2flag correctly based on locale_mmap.
905#endif
906 base->code2flag = __code2flag;
907
908 _locale_set_l((unsigned char*) C_LOCALE_SELECTOR, base);
909}
910
911void _locale_init(void) attribute_hidden;
912void _locale_init(void)
913{
914 /* TODO: mmap the locale file */
915
916 /* TODO - ??? */
917 _locale_init_l(__global_locale);
918}
919
920#endif
921/**********************************************************************/
922#if defined(L_nl_langinfo) || defined(L_nl_langinfo_l)
923
924#ifdef __LOCALE_C_ONLY
925
926/* We need to index 320 bytes of data, so you might initially think we
927 * need to store the offsets in shorts. But since the offset of the
928 * 64th item is 182, we'll store "offset - 2*64" for all items >= 64
929 * and always calculate the data offset as "offset[i] + 2*(i & 64)".
930 * This allows us to pack the data offsets in an unsigned char while
931 * also avoiding an "if".
932 *
933 * Note: Category order is assumed to be:
934 * ctype, numeric, monetary, time, collate, messages, all
935 */
936
937#define C_LC_ALL 6
938
939/* Combine the data to avoid size penalty for seperate char arrays when
940 * compiler aligns objects. The original code is left in as documentation. */
941#define cat_start nl_data
942#define C_locale_data (nl_data + C_LC_ALL + 1 + 90)
943
944static const unsigned char nl_data[C_LC_ALL + 1 + 90 + 320] = {
945/* static const char cat_start[LC_ALL + 1] = { */
946 '\x00', '\x0b', '\x0e', '\x24', '\x56', '\x56', '\x5a',
947/* }; */
948/* static const char item_offset[90] = { */
949 '\x00', '\x02', '\x04', '\x06', '\x08', '\x0a', '\x0c', '\x0e',
950 '\x10', '\x12', '\x14', '\x1a', '\x1b', '\x1b', '\x1b', '\x1b',
951 '\x1b', '\x1b', '\x1b', '\x1b', '\x1b', '\x1c', '\x1c', '\x1c',
952 '\x1c', '\x1c', '\x1c', '\x1c', '\x1c', '\x1c', '\x1c', '\x1c',
953 '\x1c', '\x1c', '\x1c', '\x1e', '\x20', '\x24', '\x28', '\x2c',
954 '\x30', '\x34', '\x38', '\x3c', '\x43', '\x4a', '\x52', '\x5c',
955 '\x65', '\x6c', '\x75', '\x79', '\x7d', '\x81', '\x85', '\x89',
956 '\x8d', '\x91', '\x95', '\x99', '\x9d', '\xa1', '\xa5', '\xad',
957 '\x36', '\x3c', '\x42', '\x46', '\x4b', '\x50', '\x57', '\x61',
958 '\x69', '\x72', '\x7b', '\x7e', '\x81', '\x96', '\x9f', '\xa8',
959 '\xb3', '\xb3', '\xb3', '\xb3', '\xb3', '\xb3', '\xb4', '\xba',
960 '\xbf', '\xbf',
961/* }; */
962/* static const char C_locale_data[320] = { */
963 '0', '\x00', '1', '\x00', '2', '\x00', '3', '\x00',
964 '4', '\x00', '5', '\x00', '6', '\x00', '7', '\x00',
965 '8', '\x00', '9', '\x00', 'A', 'S', 'C', 'I',
966 'I', '\x00', '.', '\x00', '\x7f', '\x00', '-', '\x00',
967 'S', 'u', 'n', '\x00', 'M', 'o', 'n', '\x00',
968 'T', 'u', 'e', '\x00', 'W', 'e', 'd', '\x00',
969 'T', 'h', 'u', '\x00', 'F', 'r', 'i', '\x00',
970 'S', 'a', 't', '\x00', 'S', 'u', 'n', 'd',
971 'a', 'y', '\x00', 'M', 'o', 'n', 'd', 'a',
972 'y', '\x00', 'T', 'u', 'e', 's', 'd', 'a',
973 'y', '\x00', 'W', 'e', 'd', 'n', 'e', 's',
974 'd', 'a', 'y', '\x00', 'T', 'h', 'u', 'r',
975 's', 'd', 'a', 'y', '\x00', 'F', 'r', 'i',
976 'd', 'a', 'y', '\x00', 'S', 'a', 't', 'u',
977 'r', 'd', 'a', 'y', '\x00', 'J', 'a', 'n',
978 '\x00', 'F', 'e', 'b', '\x00', 'M', 'a', 'r',
979 '\x00', 'A', 'p', 'r', '\x00', 'M', 'a', 'y',
980 '\x00', 'J', 'u', 'n', '\x00', 'J', 'u', 'l',
981 '\x00', 'A', 'u', 'g', '\x00', 'S', 'e', 'p',
982 '\x00', 'O', 'c', 't', '\x00', 'N', 'o', 'v',
983 '\x00', 'D', 'e', 'c', '\x00', 'J', 'a', 'n',
984 'u', 'a', 'r', 'y', '\x00', 'F', 'e', 'b',
985 'r', 'u', 'a', 'r', 'y', '\x00', 'M', 'a',
986 'r', 'c', 'h', '\x00', 'A', 'p', 'r', 'i',
987 'l', '\x00', 'M', 'a', 'y', '\x00', 'J', 'u',
988 'n', 'e', '\x00', 'J', 'u', 'l', 'y', '\x00',
989 'A', 'u', 'g', 'u', 's', 't', '\x00', 'S',
990 'e', 'p', 't', 'e', 'm', 'b', 'e', 'r',
991 '\x00', 'O', 'c', 't', 'o', 'b', 'e', 'r',
992 '\x00', 'N', 'o', 'v', 'e', 'm', 'b', 'e',
993 'r', '\x00', 'D', 'e', 'c', 'e', 'm', 'b',
994 'e', 'r', '\x00', 'A', 'M', '\x00', 'P', 'M',
995 '\x00', '%', 'a', ' ', '%', 'b', ' ', '%',
996 'e', ' ', '%', 'H', ':', '%', 'M', ':',
997 '%', 'S', ' ', '%', 'Y', '\x00', '%', 'm',
998 '/', '%', 'd', '/', '%', 'y', '\x00', '%',
999 'H', ':', '%', 'M', ':', '%', 'S', '\x00',
1000 '%', 'I', ':', '%', 'M', ':', '%', 'S',
1001 ' ', '%', 'p', '\x00', '^', '[', 'y', 'Y',
1002 ']', '\x00', '^', '[', 'n', 'N', ']', '\x00',
1003};
1004
1005char *nl_langinfo(nl_item item)
1006{
1007 unsigned int c;
1008 unsigned int i;
1009
1010 if ((c = _NL_ITEM_CATEGORY(item)) < C_LC_ALL) {
1011 if ((i = cat_start[c] + _NL_ITEM_INDEX(item)) < cat_start[c+1]) {
1012/* return (char *) C_locale_data + item_offset[i] + (i & 64); */
1013 return (char *) C_locale_data + nl_data[C_LC_ALL+1+i] + 2*(i & 64);
1014 }
1015 }
1016 return (char *) cat_start; /* Conveniently, this is the empty string. */
1017}
1018libc_hidden_def(nl_langinfo)
1019
1020#else /* __LOCALE_C_ONLY */
1021
1022#if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE)
1023
1024
1025
1026char *nl_langinfo(nl_item item)
1027{
1028 return nl_langinfo_l(item, __UCLIBC_CURLOCALE);
1029}
1030libc_hidden_def(nl_langinfo)
1031
1032#else /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
1033
1034libc_hidden_proto(__XL_NPP(nl_langinfo))
1035
1036static const char empty[] = "";
1037
1038char *__XL_NPP(nl_langinfo)(nl_item item __LOCALE_PARAM )
1039{
1040 unsigned int c = _NL_ITEM_CATEGORY(item);
1041 unsigned int i = _NL_ITEM_INDEX(item);
1042
1043 if ((c < LC_ALL) && (i < __LOCALE_PTR->category_item_count[c])) {
1044 return ((char **)(((char *) __LOCALE_PTR)
1045 + __LOCALE_PTR->category_offsets[c]))[i];
1046 }
1047
1048 return (char *) empty;
1049}
1050libc_hidden_def(__XL_NPP(nl_langinfo))
1051
1052#endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
1053
1054#endif /* __LOCALE_C_ONLY */
1055
1056#endif
1057/**********************************************************************/
1058#ifdef L_newlocale
1059
1060#warning mask defines for extra locale categories
1061
1062#ifdef __UCLIBC_MJN3_ONLY__
1063#warning TODO: Move posix and utf8 strings.
1064#endif
1065static const char posix[] = "POSIX";
1066static const char utf8[] = "UTF-8";
1067
1068static int find_locale(int category_mask, const char *p,
1069 unsigned char *new_locale)
1070{
1071 int i;
1072 const unsigned char *s;
1073 uint16_t n;
1074 unsigned char lang_cult, codeset;
1075
1076#if defined(__LOCALE_DATA_AT_MODIFIERS_LENGTH) && 1
1077 /* Support standard locale handling for @-modifiers. */
1078
1079#ifdef __UCLIBC_MJN3_ONLY__
1080#warning REMINDER: Fix buf size in find_locale.
1081#endif
1082 char buf[18]; /* TODO: 7+{max codeset name length} */
1083 const char *q;
1084
1085 if ((q = strchr(p,'@')) != NULL) {
1086 if ((((size_t)((q-p)-5)) > (sizeof(buf) - 5)) || (p[2] != '_')) {
1087 return 0;
1088 }
1089 /* locale name at least 5 chars long and 3rd char is '_' */
1090 s = LOCALE_AT_MODIFIERS;
1091 do {
1092 if (!strcmp((char*) (s + 2), q + 1)) {
1093 break;
1094 }
1095 s += 2 + *s; /* TODO - fix this throughout */
1096 } while (*s);
1097 if (!*s) {
1098 return 0;
1099 }
1100 assert(q - p < sizeof(buf));
1101 memcpy(buf, p, q-p);
1102 buf[q-p] = 0;
1103 buf[2] = s[1];
1104 p = buf;
1105 }
1106#endif
1107
1108 lang_cult = codeset = 0; /* Assume C and default codeset. */
1109 if (((*p == 'C') && !p[1]) || !strcmp(p, posix)) {
1110 goto FIND_LOCALE;
1111 }
1112
1113 if ((strlen(p) > 5) && (p[5] == '.')) { /* Codeset in locale name? */
1114 /* TODO: maybe CODESET_LIST + *s ??? */
1115 /* 7bit is 1, UTF-8 is 2, 8-bit is >= 3 */
1116 codeset = 2;
1117 if (strcasecmp(utf8, p + 6) != 0) {/* TODO - fix! */
1118 s = CODESET_LIST;
1119 do {
1120 ++codeset; /* Increment codeset first. */
1121 if (!strcmp((char*) CODESET_LIST + *s, p + 6)) {
1122 goto FIND_LANG_CULT;
1123 }
1124 } while (*++s);
1125 return 0; /* No matching codeset! */
1126 }
1127 }
1128
1129 FIND_LANG_CULT: /* Find language_culture number. */
1130 s = LOCALE_NAMES;
1131 do { /* TODO -- do a binary search? */
1132 /* TODO -- fix gen_mmap!*/
1133 ++lang_cult; /* Increment first since C/POSIX is 0. */
1134 if (!strncmp((char*) s, p, 5)) { /* Found a matching locale name; */
1135 goto FIND_LOCALE;
1136 }
1137 s += 5;
1138 } while (lang_cult < __LOCALE_DATA_NUM_LOCALE_NAMES);
1139 return 0; /* No matching language_culture! */
1140
1141 FIND_LOCALE: /* Find locale row matching name and codeset */
1142 s = LOCALES;
1143 n = 0;
1144 do { /* TODO -- do a binary search? */
1145 if ((lang_cult == *s) && ((codeset == s[1]) || (codeset == s[2]))) {
1146 i = 1;
1147 s = new_locale + 1;
1148 do {
1149 if (category_mask & i) {
1150 /* Encode current locale row number. */
1151 ((unsigned char *) s)[0] = (n >> 7) | 0x80;
1152 ((unsigned char *) s)[1] = (n & 0x7f) | 0x80;
1153 }
1154 s += 2;
1155 i += i;
1156 } while (i < (1 << LC_ALL));
1157
1158 return i; /* Return non-zero */
1159 }
1160 s += __LOCALE_DATA_WIDTH_LOCALES;
1161 ++n;
1162 } while (n <= __LOCALE_DATA_NUM_LOCALES); /* We started at 1!!! */
1163
1164 return 0; /* Unsupported locale. */
1165}
1166
1167static unsigned char *composite_locale(int category_mask, const char *locale,
1168 unsigned char *new_locale)
1169{
1170 char buf[MAX_LOCALE_STR];
1171 char *t;
1172 char *e;
1173 int c;
1174 int component_mask;
1175
1176 if (!strchr(locale,'=')) {
1177 if (!find_locale(category_mask, locale, new_locale)) {
1178 return NULL;
1179 }
1180 return new_locale;
1181 }
1182
1183 if (strlen(locale) >= sizeof(buf)) {
1184 return NULL;
1185 }
1186 stpcpy(buf, locale);
1187
1188 component_mask = 0;
1189 t = strtok_r(buf, "=", &e); /* This can't fail because of strchr test above. */
1190 do {
1191 c = 0;
1192 /* CATEGORY_NAMES is unsigned char* */
1193 while (strcmp((char*) CATEGORY_NAMES + (int) CATEGORY_NAMES[c], t)) {
1194 if (++c == LC_ALL) { /* Unknown category name! */
1195 return NULL;
1196 }
1197 }
1198 t = strtok_r(NULL, ";", &e);
1199 c = (1 << c);
1200 if (component_mask & c) { /* Multiple components for one category. */
1201 return NULL;
1202 }
1203 component_mask |= c;
1204 if ((category_mask & c) && (!t || !find_locale(c, t, new_locale))) {
1205 return NULL;
1206 }
1207 } while ((t = strtok_r(NULL, "=", &e)) != NULL);
1208
1209 if (category_mask & ~component_mask) { /* Category component(s) missing. */
1210 return NULL;
1211 }
1212
1213 return new_locale;
1214}
1215
1216__locale_t newlocale(int category_mask, const char *locale, __locale_t base)
1217{
1218 const char *p;
1219 int i, j, k;
1220 unsigned char new_selector[LOCALE_SELECTOR_SIZE];
1221
1222 if (category_mask == (1 << LC_ALL)) {
1223 category_mask = LC_ALL_MASK;
1224 }
1225
1226 if (!locale || ((unsigned)(category_mask) > LC_ALL_MASK)) {
1227 INVALID:
1228 __set_errno(EINVAL);
1229 return NULL; /* No locale or illegal/unsupported category. */
1230 }
1231
1232#ifdef __UCLIBC_MJN3_ONLY__
1233#warning TODO: Rename cur_locale to locale_selector.
1234#endif
1235 strcpy((char *) new_selector,
1236 (base ? (char *) base->cur_locale : C_LOCALE_SELECTOR));
1237
1238 if (!locale[0]) { /* locale == "", so check environment. */
1239 const char *envstr[4];
1240
1241 envstr[0] = "LC_ALL";
1242 envstr[1] = NULL;
1243 envstr[2] = "LANG";
1244 envstr[3] = posix;
1245
1246 i = 1;
1247 k = 0;
1248 do {
1249 if (category_mask & i) {
1250 /* Note: SUSv3 doesn't define a fallback mechanism here.
1251 * So, if LC_ALL is invalid, we do _not_ continue trying
1252 * the other environment vars. */
1253 envstr[1] = (char*) CATEGORY_NAMES + CATEGORY_NAMES[k];
1254 j = 0;
1255 while (1) {
1256 p = envstr[j];
1257 if (++j >= 4)
1258 break; /* now p == "POSIX" */
1259 p = getenv(p);
1260 if (p && p[0])
1261 break;
1262 };
1263
1264 /* The user set something... is it valid? */
1265 /* Note: Since we don't support user-supplied locales and
1266 * alternate paths, we don't need to worry about special
1267 * handling for suid/sgid apps. */
1268 if (!find_locale(i, p, new_selector)) {
1269 goto INVALID;
1270 }
1271 }
1272 i += i;
1273 } while (++k < LC_ALL);
1274 } else if (!composite_locale(category_mask, locale, new_selector)) {
1275 goto INVALID;
1276 }
1277
1278#ifdef __UCLIBC_MJN3_ONLY__
1279#warning TODO: Do a compatible codeset check!
1280#endif
1281
1282 /* If we get here, the new selector corresponds to a valid locale. */
1283
1284#ifdef __UCLIBC_MJN3_ONLY__
1285#warning CONSIDER: Probably want a _locale_new func to allow for caching of locales.
1286#endif
1287#if 0
1288 if (base) {
1289 _locale_set_l(new_selector, base);
1290 } else {
1291 base = _locale_new(new_selector);
1292 }
1293#else
1294 if (!base) {
1295 base = calloc(1, sizeof(struct __uclibc_locale_struct));
1296 if (base == NULL)
1297 return base;
1298 _locale_init_l(base);
1299 }
1300
1301 _locale_set_l(new_selector, base);
1302#endif
1303
1304 return base;
1305}
1306#ifdef __UCLIBC_HAS_XLOCALE__
1307libc_hidden_def(newlocale)
1308#endif
1309
1310#endif
1311/**********************************************************************/
1312#ifdef L_duplocale
1313
1314
1315#ifdef __UCLIBC_MJN3_ONLY__
1316#warning REMINDER: When we allocate ctype tables, remember to dup them.
1317#endif
1318
1319__locale_t duplocale(__locale_t dataset)
1320{
1321 __locale_t r;
1322 uint16_t * i2w;
1323 size_t n;
1324
1325 assert(dataset != LC_GLOBAL_LOCALE);
1326
1327 r = malloc(sizeof(struct __uclibc_locale_struct));
1328 if (r != NULL) {
1329 n = 2 * dataset->collate.max_col_index + 2;
1330 i2w = calloc(n, sizeof(uint16_t));
1331 if (i2w != NULL) {
1332 memcpy(r, dataset, sizeof(struct __uclibc_locale_struct));
1333 r->collate.index2weight = i2w;
1334 memcpy(i2w, dataset->collate.index2weight, n * sizeof(uint16_t));
1335 } else {
1336 free(r);
1337 r = NULL;
1338 }
1339 }
1340 return r;
1341}
1342
1343#endif
1344/**********************************************************************/
1345#ifdef L_freelocale
1346
1347#ifdef __UCLIBC_MJN3_ONLY__
1348#warning REMINDER: When we allocate ctype tables, remember to free them.
1349#endif
1350
1351void freelocale(__locale_t dataset)
1352{
1353 assert(dataset != __global_locale);
1354 assert(dataset != LC_GLOBAL_LOCALE);
1355
1356 free(dataset->collate.index2weight); /* Free collation data. */
1357 free(dataset); /* Free locale */
1358}
1359
1360#endif
1361/**********************************************************************/
1362#ifdef L_uselocale
1363
1364__locale_t uselocale(__locale_t dataset)
1365{
1366 __locale_t old;
1367
1368 if (!dataset) {
1369 old = __UCLIBC_CURLOCALE;
1370 } else {
1371 if (dataset == LC_GLOBAL_LOCALE) {
1372 dataset = __global_locale;
1373 }
1374#ifdef __UCLIBC_HAS_THREADS__
1375 old = __curlocale_set(dataset);
1376#else
1377 old = __curlocale_var;
1378 __curlocale_var = dataset;
1379#endif
1380 }
1381
1382 if (old == __global_locale) {
1383 return LC_GLOBAL_LOCALE;
1384 }
1385 return old;
1386}
1387libc_hidden_def(uselocale)
1388
1389#endif
1390/**********************************************************************/
1391#ifdef L___curlocale
1392
1393#ifdef __UCLIBC_HAS_THREADS__
1394
1395__locale_t weak_const_function __curlocale(void)
1396{
1397 return __curlocale_var; /* This is overriden by the thread version. */
1398}
1399
1400__locale_t weak_function __curlocale_set(__locale_t newloc)
1401{
1402 __locale_t oldloc = __curlocale_var;
1403 assert(newloc != LC_GLOBAL_LOCALE);
1404 __curlocale_var = newloc;
1405 return oldloc;
1406}
1407
1408#endif
1409
1410#endif
1411/**********************************************************************/
1412#ifdef L___locale_mbrtowc_l
1413
1414/* NOTE: This returns an int... not size_t. Also, it is not a general
1415 * routine. It is actually a very stripped-down version of mbrtowc
1416 * that takes a __locale_t arg. This is used by strcoll and strxfrm.
1417 * It is also used above to generate wchar_t versions of the decimal point
1418 * and thousands seperator. */
1419
1420
1421#ifndef __CTYPE_HAS_UTF_8_LOCALES
1422#warning __CTYPE_HAS_UTF_8_LOCALES not set!
1423#endif
1424#ifndef __CTYPE_HAS_8_BIT_LOCALES
1425#warning __CTYPE_HAS_8_BIT_LOCALES not set!
1426#endif
1427
1428#define Cc2wc_IDX_SHIFT __LOCALE_DATA_Cc2wc_IDX_SHIFT
1429#define Cc2wc_ROW_LEN __LOCALE_DATA_Cc2wc_ROW_LEN
1430
1431extern size_t _wchar_utf8sntowcs(wchar_t *__restrict pwc, size_t wn,
1432 const char **__restrict src, size_t n,
1433 mbstate_t *ps, int allow_continuation) attribute_hidden;
1434
1435int attribute_hidden __locale_mbrtowc_l(wchar_t *__restrict dst,
1436 const char *__restrict src,
1437 __locale_t loc )
1438{
1439#ifdef __CTYPE_HAS_UTF_8_LOCALES
1440 if (loc->encoding == __ctype_encoding_utf8) {
1441 mbstate_t ps;
1442 const char *p = src;
1443 size_t r;
1444 ps.__mask = 0;
1445 r = _wchar_utf8sntowcs(dst, 1, &p, SIZE_MAX, &ps, 1);
1446 return (r == 1) ? (p-src) : r; /* Need to return 0 if nul char. */
1447 }
1448#endif
1449
1450#ifdef __CTYPE_HAS_8_BIT_LOCALES
1451 assert((loc->encoding == __ctype_encoding_7_bit) || (loc->encoding == __ctype_encoding_8_bit));
1452#else
1453 assert(loc->encoding == __ctype_encoding_7_bit);
1454#endif
1455
1456 if ((*dst = ((unsigned char)(*src))) < 0x80) { /* ASCII... */
1457 return (*src != 0);
1458 }
1459
1460#ifdef __CTYPE_HAS_8_BIT_LOCALES
1461 if (loc->encoding == __ctype_encoding_8_bit) {
1462 wchar_t wc = *dst - 0x80;
1463 *dst = loc->tbl8c2wc[
1464 (loc->idx8c2wc[wc >> Cc2wc_IDX_SHIFT]
1465 << Cc2wc_IDX_SHIFT) + (wc & (Cc2wc_ROW_LEN - 1))];
1466 if (*dst) {
1467 return 1;
1468 }
1469 }
1470#endif
1471
1472 return -1;
1473}
1474
1475#endif
1476/**********************************************************************/