OSDN Git Service

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