/* Copyright (C) 2002 Manuel Novoa III
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, see
* .
*/
/* Nov. 1, 2002
* Reworked setlocale() return values and locale arg processing to
* be more like glibc. Applications expecting to be able to
* query locale settings should now work... at the cost of almost
* doubling the size of the setlocale object code.
* Fixed a bug in the internal fixed-size-string locale specifier code.
*
* Dec 20, 2002
* Added in collation support and updated stub nl_langinfo.
*
* Aug 1, 2003
* Added glibc-like extended locale stuff (newlocale, duplocale, etc).
*
* Aug 18, 2003
* Bug in duplocale... collation data wasn't copied.
* Bug in newlocale... translate 1<
#include
#include
#include
#include
#include
#include
#include
#include
#undef __LOCALE_C_ONLY
#ifndef __UCLIBC_HAS_LOCALE__
#define __LOCALE_C_ONLY
#endif /* __UCLIBC_HAS_LOCALE__ */
#ifdef __LOCALE_C_ONLY
#include
#else /* __LOCALE_C_ONLY */
/* Need to include this before locale.h! */
#include
#undef CODESET_LIST
#define CODESET_LIST (__locale_mmap->codeset_list)
#ifdef __UCLIBC_HAS_XLOCALE__
#include
#else /* __UCLIBC_HAS_XLOCALE__ */
/* We need this internally... */
#define __UCLIBC_HAS_XLOCALE__ 1
#include
#undef __UCLIBC_HAS_XLOCALE__
#endif /* __UCLIBC_HAS_XLOCALE__ */
#include
#define LOCALE_NAMES (__locale_mmap->locale_names5)
#define LOCALES (__locale_mmap->locales)
#define LOCALE_AT_MODIFIERS (__locale_mmap->locale_at_modifiers)
#define CATEGORY_NAMES (__locale_mmap->lc_names)
#define MAX_LOCALE_STR 256 /* TODO: Only sufficient for current case. */
#define MAX_LOCALE_CATEGORY_STR 32 /* TODO: Only sufficient for current case. */
/* Note: Best if MAX_LOCALE_CATEGORY_STR is a power of 2. */
extern int _locale_set_l(const unsigned char *p, __locale_t base) attribute_hidden;
extern void _locale_init_l(__locale_t base) attribute_hidden;
#endif /* __LOCALE_C_ONLY */
#undef LOCALE_STRING_SIZE
#define LOCALE_SELECTOR_SIZE (2 * __LC_ALL + 2)
#define C_LOCALE_SELECTOR "\x23\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80"
#include
#include
/**********************************************************************/
#ifdef L_setlocale
#ifdef __LOCALE_C_ONLY
static const char C_string[] = "C";
char *setlocale(int category, register const char *locale)
{
return ( (((unsigned int)(category)) <= LC_ALL)
&& ( (!locale) /* Request for locale category string. */
|| (!*locale) /* Implementation-defined default is C. */
|| ((*locale == 'C') && !locale[1])
|| (!strcmp(locale, "POSIX"))) )
? (char *) C_string /* Always in C/POSIX locale. */
: NULL;
}
#else /* ---------------------------------------------- __LOCALE_C_ONLY */
#if !defined(__LOCALE_DATA_NUM_LOCALES) || (__LOCALE_DATA_NUM_LOCALES <= 1)
#error locales enabled, but not data other than for C locale!
#endif
static const char posix[] = "POSIX";
static const char utf8[] = "UTF-8";
/* Individual category strings start at hr_locale + category * MAX_LOCALE_CATEGORY.
* This holds for LC_ALL as well.
*/
static char hr_locale[(MAX_LOCALE_CATEGORY_STR * LC_ALL) + MAX_LOCALE_STR];
static void update_hr_locale(const unsigned char *spec)
{
const unsigned char *loc;
const unsigned char *s;
char *n;
int i, category, done;
done = category = 0;
do {
s = spec + 1;
n = hr_locale + category * MAX_LOCALE_CATEGORY_STR;
if (category == LC_ALL) {
done = 1;
for (i = 0 ; i < LC_ALL-1 ; i += 2) {
if ((s[i] != s[i+2]) || (s[i+1] != s[i+3])) {
goto SKIP;
}
}
/* All categories the same, so simplify string by using a single
* category. */
category = LC_CTYPE;
}
SKIP:
i = (category == LC_ALL) ? 0 : category;
s += 2*i;
do {
if ((*s != 0xff) || (s[1] != 0xff)) {
loc = LOCALES
+ __LOCALE_DATA_WIDTH_LOCALES * ((((int)(*s & 0x7f)) << 7)
+ (s[1] & 0x7f));
if (category == LC_ALL) {
/* CATEGORY_NAMES is unsigned char* */
n = stpcpy(n, (char*) CATEGORY_NAMES + (int) CATEGORY_NAMES[i]);
*n++ = '=';
}
if (*loc == 0) {
*n++ = 'C';
*n = 0;
} else {
char at = 0;
memcpy(n, LOCALE_NAMES + 5*((*loc)-1), 5);
if (n[2] != '_') {
at = n[2];
n[2] = '_';
}
n += 5;
*n++ = '.';
if (loc[2] == 2) {
n = stpcpy(n, utf8);
} else if (loc[2] >= 3) {
n = stpcpy(n, (char*) CODESET_LIST + (int)(CODESET_LIST[loc[2] - 3]));
}
if (at) {
const char *q;
*n++ = '@';
q = (char*) LOCALE_AT_MODIFIERS;
do {
if (q[1] == at) {
n = stpcpy(n, q+2);
break;
}
q += 2 + *q;
} while (*q);
}
}
*n++ = ';';
}
s += 2;
} while (++i < category);
*--n = 0; /* Remove trailing ';' and nul-terminate. */
++category;
} while (!done);
}
char *setlocale(int category, const char *locale)
{
if (((unsigned int)(category)) > LC_ALL) {
#if 0
__set_errno(EINVAL); /* glibc sets errno -- SUSv3 doesn't say. */
#endif
return NULL; /* Illegal/unsupported category. */
}
if (locale != NULL) { /* Not just a query... */
if (!newlocale((1 << category), locale, __global_locale)) {
return NULL; /* Failed! */
}
update_hr_locale(__global_locale->cur_locale);
}
/* Either a query or a successful set, so return current locale string. */
return hr_locale + (category * MAX_LOCALE_CATEGORY_STR);
}
#endif /* __LOCALE_C_ONLY */
#endif
/**********************************************************************/
#ifdef L_localeconv
/* Note: We assume here that the compiler does the sane thing regarding
* placement of the fields in the struct. If necessary, we could ensure
* this usings an array of offsets but at some size cost. */
#ifdef __LOCALE_C_ONLY
static struct lconv the_lconv;
static const char decpt[] = ".";
struct lconv *localeconv(void)
{
register char *p = (char *)(&the_lconv);
*((char **)p) = (char *) decpt;
do {
p += sizeof(char **);
*((char **)p) = (char *) (decpt+1);
} while (p < (char *) &the_lconv.negative_sign);
p = (&the_lconv.int_frac_digits);
do {
*p = CHAR_MAX;
++p;
} while (p <= &the_lconv.int_n_sign_posn);
return &the_lconv;
}
#else /* __LOCALE_C_ONLY */
static struct lconv the_lconv;
struct lconv *localeconv(void)
{
register char *p = (char *) &the_lconv;
register char **q = (char **) &(__UCLIBC_CURLOCALE->decimal_point);
do {
*((char **)p) = *q;
p += sizeof(char **);
++q;
} while (p < &the_lconv.int_frac_digits);
do {
*p = **q;
++p;
++q;
} while (p <= &the_lconv.int_n_sign_posn);
return &the_lconv;
}
#endif /* __LOCALE_C_ONLY */
libc_hidden_def(localeconv)
#endif
/**********************************************************************/
#if defined(L__locale_init) && !defined(__LOCALE_C_ONLY)
struct __uclibc_locale_struct __global_locale_data;
__locale_t __global_locale = &__global_locale_data;
#ifdef __UCLIBC_HAS_XLOCALE__
__locale_t __curlocale_var = &__global_locale_data;
#endif
/*----------------------------------------------------------------------*/
static const char utf8[] = "UTF-8";
static const char ascii[] = "ASCII";
typedef struct {
uint16_t num_base;
uint16_t num_der;
uint16_t MAX_WEIGHTS;
uint16_t num_index2weight;
#define num_index2ruleidx num_index2weight
uint16_t num_weightstr;
uint16_t num_multistart;
uint16_t num_override;
uint16_t num_ruletable;
} coldata_header_t;
typedef struct {
uint16_t num_weights;
uint16_t num_starters;
uint16_t ii_shift;
uint16_t ti_shift;
uint16_t ii_len;
uint16_t ti_len;
uint16_t max_weight;
uint16_t num_col_base;
uint16_t max_col_index;
uint16_t undefined_idx;
uint16_t range_low;
uint16_t range_count;
uint16_t range_base_weight;
uint16_t range_rule_offset;
uint16_t index2weight_offset;
uint16_t index2ruleidx_offset;
uint16_t multistart_offset;
uint16_t wcs2colidt_offset_low;
uint16_t wcs2colidt_offset_hi;
} coldata_base_t;
typedef struct {
uint16_t base_idx;
uint16_t undefined_idx;
uint16_t overrides_offset;
uint16_t multistart_offset;
} coldata_der_t;
static int init_cur_collate(int der_num, __collate_t *cur_collate)
{
const uint16_t *__locale_collate_tbl = __locale_mmap->collate_data;
coldata_header_t *cdh;
coldata_base_t *cdb;
coldata_der_t *cdd;
const uint16_t *p;
size_t n;
uint16_t i, w;
#if 0
assert(sizeof(coldata_base_t) == 19*2);
assert(sizeof(coldata_der_t) == 4*2);
assert(sizeof(coldata_header_t) == 8*2);
#endif
if (!der_num) { /* C locale... special */
cur_collate->num_weights = 0;
return 1;
}
--der_num;
cdh = (coldata_header_t *) __locale_collate_tbl;
#if 0
if (der_num >= cdh->num_der) {
return 0;
}
#else
assert((der_num < cdh->num_der));
#endif
cdd = (coldata_der_t *)(__locale_collate_tbl
+ (sizeof(coldata_header_t)
+ cdh->num_base * sizeof(coldata_base_t)
+ der_num * sizeof(coldata_der_t)
)/2 );
cdb = (coldata_base_t *)(__locale_collate_tbl
+ (sizeof(coldata_header_t)
+ cdd->base_idx * sizeof(coldata_base_t)
)/2 );
memcpy(cur_collate, cdb, offsetof(coldata_base_t,index2weight_offset));
cur_collate->undefined_idx = cdd->undefined_idx;
cur_collate->ti_mask = (1 << cur_collate->ti_shift)-1;
cur_collate->ii_mask = (1 << cur_collate->ii_shift)-1;
/* fflush(stdout); */
/* fprintf(stderr,"base=%d num_col_base: %d %d\n", cdd->base_idx ,cur_collate->num_col_base, cdb->num_col_base); */
n = (sizeof(coldata_header_t) + cdh->num_base * sizeof(coldata_base_t)
+ cdh->num_der * sizeof(coldata_der_t))/2;
/* fprintf(stderr,"n = %d\n", n); */
cur_collate->index2weight_tbl = __locale_collate_tbl + n + cdb->index2weight_offset;
/* fprintf(stderr,"i2w = %d\n", n + cdb->index2weight_offset); */
n += cdh->num_index2weight;
cur_collate->index2ruleidx_tbl = __locale_collate_tbl + n + cdb->index2ruleidx_offset;
/* fprintf(stderr,"i2r = %d\n", n + cdb->index2ruleidx_offset); */
n += cdh->num_index2ruleidx;
cur_collate->multistart_tbl = __locale_collate_tbl + n + cdd->multistart_offset;
/* fprintf(stderr,"mts = %d\n", n + cdb->multistart_offset); */
n += cdh->num_multistart;
cur_collate->overrides_tbl = __locale_collate_tbl + n + cdd->overrides_offset;
/* fprintf(stderr,"ovr = %d\n", n + cdd->overrides_offset); */
n += cdh->num_override;
cur_collate->ruletable = __locale_collate_tbl + n;
/* fprintf(stderr, "rtb = %d\n", n); */
n += cdh->num_ruletable;
cur_collate->weightstr = __locale_collate_tbl + n;
/* fprintf(stderr,"wts = %d\n", n); */
n += cdh->num_weightstr;
cur_collate->wcs2colidt_tbl = __locale_collate_tbl + n
+ (((unsigned long)(cdb->wcs2colidt_offset_hi)) << 16)
+ cdb->wcs2colidt_offset_low;
/* fprintf(stderr,"wcs = %lu\n", n + (((unsigned long)(cdb->wcs2colidt_offset_hi)) << 16) */
/* + cdb->wcs2colidt_offset_low); */
cur_collate->MAX_WEIGHTS = cdh->MAX_WEIGHTS;
cur_collate->index2weight = calloc(2*cur_collate->max_col_index+2,
sizeof(uint16_t));
if (!cur_collate->index2weight) {
return 0;
}
cur_collate->index2ruleidx = cur_collate->index2weight
+ cur_collate->max_col_index + 1;
memcpy(cur_collate->index2weight, cur_collate->index2weight_tbl,
cur_collate->num_col_base * sizeof(uint16_t));
memcpy(cur_collate->index2ruleidx, cur_collate->index2ruleidx_tbl,
cur_collate->num_col_base * sizeof(uint16_t));
/* now do the overrides */
p = cur_collate->overrides_tbl;
while (*p > 1) {
/* fprintf(stderr, "processing override -- count = %d\n", *p); */
n = *p++;
w = *p++;
do {
i = *p++;
/* fprintf(stderr, " i=%d (%#x) w=%d *p=%d\n", i, i, w, *p); */
cur_collate->index2weight[i-1] = w++;
cur_collate->index2ruleidx[i-1] = *p++;
} while (--n);
}
assert(*p == 1);
while (*++p) {
i = *p;
/* fprintf(stderr, " i=%d (%#x) w=%d *p=%d\n", i, i, p[1], p[2]); */
cur_collate->index2weight[i-1] = *++p;
cur_collate->index2ruleidx[i-1] = *++p;
}
for (i=0 ; i < cur_collate->multistart_tbl[0] ; i++) {
p = cur_collate->multistart_tbl;
/* fprintf(stderr, "%2d of %2d: %d ", i, cur_collate->multistart_tbl[0], p[i]); */
p += p[i];
do {
n = *p++;
do {
if (!*p) { /* found it */
/* fprintf(stderr, "found: n=%d (%#lx) |%.*ls|\n", n, (int) *cs->s, n, cs->s); */
/* fprintf(stderr, ": %d - single\n", n); */
goto FOUND;
}
/* the lookup check here is safe since we're assured that *p is a valid colidex */
/* fprintf(stderr, "lookup(%lc)==%d *p==%d\n", cs->s[n], lookup(cs->s[n]), (int) *p); */
/* fprintf(stderr, ": %d - ", n); */
do {
/* fprintf(stderr, "%d|", *p); */
} while (*p++);
break;
} while (1);
} while (1);
FOUND:
continue;
}
return 1;
}
int attribute_hidden _locale_set_l(const unsigned char *p, __locale_t base)
{
const char **x;
unsigned char *s = base->cur_locale + 1;
const size_t *stp;
const unsigned char *r;
const uint16_t *io;
const uint16_t *ii;
const unsigned char *d;
int row; /* locale row */
int crow; /* category row */
int len;
int c;
int i = 0;
__collate_t newcol;
++p;
newcol.index2weight = NULL;
if ((p[2*LC_COLLATE] != s[2*LC_COLLATE])
|| (p[2*LC_COLLATE + 1] != s[2*LC_COLLATE + 1])
) {
row = (((int)(*p & 0x7f)) << 7) + (p[1] & 0x7f);
assert(row < __LOCALE_DATA_NUM_LOCALES);
if (!init_cur_collate(__locale_mmap->locales[ __LOCALE_DATA_WIDTH_LOCALES
* row + 3 + LC_COLLATE ],
&newcol)
) {
return 0; /* calloc failed. */
}
free(base->collate.index2weight);
memcpy(&base->collate, &newcol, sizeof(__collate_t));
}
do {
if ((*p != *s) || (p[1] != s[1])) {
row = (((int)(*p & 0x7f)) << 7) + (p[1] & 0x7f);
assert(row < __LOCALE_DATA_NUM_LOCALES);
*s = *p;
s[1] = p[1];
if ((i != LC_COLLATE)
&& ((len = __locale_mmap->lc_common_item_offsets_LEN[i]) != 0)
) {
crow = __locale_mmap->locales[ __LOCALE_DATA_WIDTH_LOCALES * row
+ 3 + i ]
* len;
x = (const char **)(((char *) base)
+ base->category_offsets[i]);
stp = __locale_mmap->lc_common_tbl_offsets + 4*i;
r = (const unsigned char *)( ((char *)__locale_mmap) + *stp );
io = (const uint16_t *)( ((char *)__locale_mmap) + *++stp );
ii = (const uint16_t *)( ((char *)__locale_mmap) + *++stp );
d = (const unsigned char *)( ((char *)__locale_mmap) + *++stp );
for (c = 0; c < len; c++) {
x[c] = (char*)(d + ii[r[crow + c] + io[c]]);
}
}
if (i == LC_CTYPE) {
c = __locale_mmap->locales[ __LOCALE_DATA_WIDTH_LOCALES * row
+ 2 ]; /* codeset */
if (c <= 2) {
if (c == 2) {
base->codeset = utf8;
base->encoding = __ctype_encoding_utf8;
/* TODO - fix for bcc */
base->mb_cur_max = 6;
} else {
assert(c == 1);
base->codeset = ascii;
base->encoding = __ctype_encoding_7_bit;
base->mb_cur_max = 1;
}
} else {
const __codeset_8_bit_t *c8b;
r = CODESET_LIST;
c -= 3;
base->codeset = (char *) (r + r[c]);
base->encoding = __ctype_encoding_8_bit;
/* TODO - update when translit implemented! */
base->mb_cur_max = 1;
c8b = __locale_mmap->codeset_8_bit + c;
#ifdef __CTYPE_HAS_8_BIT_LOCALES
base->idx8ctype = c8b->idx8ctype;
base->idx8uplow = c8b->idx8uplow;
#ifdef __UCLIBC_HAS_WCHAR__
base->idx8c2wc = c8b->idx8c2wc;
base->idx8wc2c = c8b->idx8wc2c;
/* translit */
#endif /* __UCLIBC_HAS_WCHAR__ */
/* What follows is fairly bloated, but it is just a hack
* to get the 8-bit codeset ctype stuff functioning.
* All of this will be replaced in the next generation
* of locale support anyway... */
memcpy(base->__ctype_b_data,
__C_ctype_b - __UCLIBC_CTYPE_B_TBL_OFFSET,
(256 + __UCLIBC_CTYPE_B_TBL_OFFSET)
* sizeof(__ctype_mask_t));
memcpy(base->__ctype_tolower_data,
__C_ctype_tolower - __UCLIBC_CTYPE_TO_TBL_OFFSET,
(256 + __UCLIBC_CTYPE_TO_TBL_OFFSET)
* sizeof(__ctype_touplow_t));
memcpy(base->__ctype_toupper_data,
__C_ctype_toupper - __UCLIBC_CTYPE_TO_TBL_OFFSET,
(256 + __UCLIBC_CTYPE_TO_TBL_OFFSET)
* sizeof(__ctype_touplow_t));
#define Cctype_TBL_MASK ((1 << __LOCALE_DATA_Cctype_IDX_SHIFT) - 1)
#define Cctype_IDX_OFFSET (128 >> __LOCALE_DATA_Cctype_IDX_SHIFT)
{
int u;
__ctype_mask_t m;
for (u=0 ; u < 128 ; u++) {
#ifdef __LOCALE_DATA_Cctype_PACKED
c = base->tbl8ctype
[ ((int)(c8b->idx8ctype
[(u >> __LOCALE_DATA_Cctype_IDX_SHIFT) ])
<< (__LOCALE_DATA_Cctype_IDX_SHIFT - 1))
+ ((u & Cctype_TBL_MASK) >> 1)];
c = (u & 1) ? (c >> 4) : (c & 0xf);
#else
c = base->tbl8ctype
[ ((int)(c8b->idx8ctype
[(u >> __LOCALE_DATA_Cctype_IDX_SHIFT) ])
<< __LOCALE_DATA_Cctype_IDX_SHIFT)
+ (u & Cctype_TBL_MASK) ];
#endif
m = base->code2flag[c];
base->__ctype_b_data
[128 + __UCLIBC_CTYPE_B_TBL_OFFSET + u]
= m;
#ifdef __UCLIBC_HAS_CTYPE_SIGNED__
if (((signed char)(128 + u)) != -1) {
base->__ctype_b_data[__UCLIBC_CTYPE_B_TBL_OFFSET
+ ((signed char)(128 + u))]
= m;
}
#endif
base->__ctype_tolower_data
[128 + __UCLIBC_CTYPE_TO_TBL_OFFSET + u]
= 128 + u;
base->__ctype_toupper_data
[128 + __UCLIBC_CTYPE_TO_TBL_OFFSET + u]
= 128 + u;
if (m & (_ISlower|_ISupper)) {
c = base->tbl8uplow
[ ((int)(c8b->idx8uplow
[u >> __LOCALE_DATA_Cuplow_IDX_SHIFT])
<< __LOCALE_DATA_Cuplow_IDX_SHIFT)
+ ((128 + u)
& ((1 << __LOCALE_DATA_Cuplow_IDX_SHIFT)
- 1)) ];
if (m & _ISlower) {
base->__ctype_toupper_data
[128 + __UCLIBC_CTYPE_TO_TBL_OFFSET + u]
= (unsigned char)(128 + u + c);
#ifdef __UCLIBC_HAS_CTYPE_SIGNED__
if (((signed char)(128 + u)) != -1) {
base->__ctype_toupper_data
[__UCLIBC_CTYPE_TO_TBL_OFFSET
+ ((signed char)(128 + u))]
= (unsigned char)(128 + u + c);
}
#endif
} else {
base->__ctype_tolower_data
[128 + __UCLIBC_CTYPE_TO_TBL_OFFSET + u]
= (unsigned char)(128 + u - c);
#ifdef __UCLIBC_HAS_CTYPE_SIGNED__
if (((signed char)(128 + u)) != -1) {
base->__ctype_tolower_data
[__UCLIBC_CTYPE_TO_TBL_OFFSET
+ ((signed char)(128 + u))]
= (unsigned char)(128 + u - c);
}
#endif
}
}
}
}
#ifdef __UCLIBC_HAS_XLOCALE__
base->__ctype_b = base->__ctype_b_data
+ __UCLIBC_CTYPE_B_TBL_OFFSET;
base->__ctype_tolower = base->__ctype_tolower_data
+ __UCLIBC_CTYPE_TO_TBL_OFFSET;
base->__ctype_toupper = base->__ctype_toupper_data
+ __UCLIBC_CTYPE_TO_TBL_OFFSET;
#else /* __UCLIBC_HAS_XLOCALE__ */
__ctype_b = base->__ctype_b_data
+ __UCLIBC_CTYPE_B_TBL_OFFSET;
__ctype_tolower = base->__ctype_tolower_data
+ __UCLIBC_CTYPE_TO_TBL_OFFSET;
__ctype_toupper = base->__ctype_toupper_data
+ __UCLIBC_CTYPE_TO_TBL_OFFSET;
#endif /* __UCLIBC_HAS_XLOCALE__ */
#endif /* __CTYPE_HAS_8_BIT_LOCALES */
}
d = base->outdigit_length;
x = &base->outdigit0_mb;
for (c = 0 ; c < 10 ; c++) {
((unsigned char *)d)[c] = strlen(x[c]);
assert(d[c] > 0);
}
} else if (i == LC_NUMERIC) {
assert(LC_NUMERIC > LC_CTYPE); /* Need ctype initialized. */
base->decimal_point_len
= __locale_mbrtowc_l(&base->decimal_point_wc,
base->decimal_point, base);
assert(base->decimal_point_len > 0);
assert(base->decimal_point[base->decimal_point_len] == 0);
if (*base->grouping) {
base->thousands_sep_len
= __locale_mbrtowc_l(&base->thousands_sep_wc,
base->thousands_sep, base);
#if 1
assert(base->thousands_sep_len >= 0);
if (base->thousands_sep_len == 0) {
base->grouping = base->thousands_sep; /* empty string */
}
assert(base->thousands_sep[base->thousands_sep_len] == 0);
#else
assert(base->thousands_sep_len > 0);
assert(base->thousands_sep[base->thousands_sep_len] == 0);
#endif
}
/* } else if (i == LC_COLLATE) { */
/* init_cur_collate(__locale_mmap->locales[ __LOCALE_DATA_WIDTH_LOCALES */
/* * row + 3 + i ], */
/* &base->collate); */
}
}
++i;
p += 2;
s += 2;
} while (i < LC_ALL);
return 1;
}
static const uint16_t __code2flag[16] = {
0, /* unclassified = 0 */
_ISprint|_ISgraph|_ISalnum|_ISalpha, /* alpha_nonupper_nonlower */
_ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, /* alpha_lower */
_ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower|_ISupper, /* alpha_upper_lower */
_ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, /* alpha_upper */
_ISprint|_ISgraph|_ISalnum|_ISdigit, /* digit */
_ISprint|_ISgraph|_ISpunct, /* punct */
_ISprint|_ISgraph, /* graph */
_ISprint|_ISspace, /* print_space_nonblank */
_ISprint|_ISspace|_ISblank, /* print_space_blank */
_ISspace, /* space_nonblank_noncntrl */
_ISspace|_ISblank, /* space_blank_noncntrl */
_IScntrl|_ISspace, /* cntrl_space_nonblank */
_IScntrl|_ISspace|_ISblank, /* cntrl_space_blank */
_IScntrl /* cntrl_nonspace */
};
void attribute_hidden _locale_init_l(__locale_t base)
{
memset(base->cur_locale, 0, LOCALE_SELECTOR_SIZE);
base->cur_locale[0] = '#';
memcpy(base->category_item_count,
__locale_mmap->lc_common_item_offsets_LEN,
LC_ALL);
++base->category_item_count[0]; /* Increment for codeset entry. */
base->category_offsets[0] = offsetof(struct __uclibc_locale_struct, outdigit0_mb);
base->category_offsets[1] = offsetof(struct __uclibc_locale_struct, decimal_point);
base->category_offsets[2] = offsetof(struct __uclibc_locale_struct, int_curr_symbol);
base->category_offsets[3] = offsetof(struct __uclibc_locale_struct, abday_1);
/* base->category_offsets[4] = offsetof(struct __uclibc_locale_struct, collate???); */
base->category_offsets[5] = offsetof(struct __uclibc_locale_struct, yesexpr);
#ifdef __CTYPE_HAS_8_BIT_LOCALES
base->tbl8ctype
= (const unsigned char *) &__locale_mmap->tbl8ctype;
base->tbl8uplow
= (const unsigned char *) &__locale_mmap->tbl8uplow;
#ifdef __UCLIBC_HAS_WCHAR__
base->tbl8c2wc
= (const uint16_t *) &__locale_mmap->tbl8c2wc;
base->tbl8wc2c
= (const unsigned char *) &__locale_mmap->tbl8wc2c;
/* translit */
#endif /* __UCLIBC_HAS_WCHAR__ */
#endif /* __CTYPE_HAS_8_BIT_LOCALES */
#ifdef __UCLIBC_HAS_WCHAR__
base->tblwctype
= (const unsigned char *) &__locale_mmap->tblwctype;
base->tblwuplow
= (const unsigned char *) &__locale_mmap->tblwuplow;
base->tblwuplow_diff
= (const int16_t *) &__locale_mmap->tblwuplow_diff;
/* base->tblwcomb */
/* = (const unsigned char *) &__locale_mmap->tblwcomb; */
/* width?? */
#endif /* __UCLIBC_HAS_WCHAR__ */
/* Initially, set things up to use the global C ctype tables.
* This is correct for C (ASCII) and UTF-8 based locales (except tr_TR). */
#ifdef __UCLIBC_HAS_XLOCALE__
base->__ctype_b = __C_ctype_b;
base->__ctype_tolower = __C_ctype_tolower;
base->__ctype_toupper = __C_ctype_toupper;
#else /* __UCLIBC_HAS_XLOCALE__ */
__ctype_b = __C_ctype_b;
__ctype_tolower = __C_ctype_tolower;
__ctype_toupper = __C_ctype_toupper;
#endif /* __UCLIBC_HAS_XLOCALE__ */
base->code2flag = __code2flag;
_locale_set_l((unsigned char*) C_LOCALE_SELECTOR, base);
}
void _locale_init(void)
{
/* TODO: mmap the locale file */
/* TODO - ??? */
_locale_init_l(__global_locale);
}
#endif
/**********************************************************************/
#if defined(L_nl_langinfo) || defined(L_nl_langinfo_l)
#ifdef __LOCALE_C_ONLY
/* We need to index 320 bytes of data, so you might initially think we
* need to store the offsets in shorts. But since the offset of the
* 64th item is 182, we'll store "offset - 2*64" for all items >= 64
* and always calculate the data offset as "offset[i] + 2*(i & 64)".
* This allows us to pack the data offsets in an unsigned char while
* also avoiding an "if".
*
* Note: Category order is assumed to be:
* ctype, numeric, monetary, time, collate, messages, all
*/
#define C_LC_ALL 6
/* Combine the data to avoid size penalty for seperate char arrays when
* compiler aligns objects. The original code is left in as documentation. */
#define cat_start nl_data
#define C_locale_data (nl_data + C_LC_ALL + 1 + 90)
static const unsigned char nl_data[C_LC_ALL + 1 + 90 + 320] = {
/* static const char cat_start[LC_ALL + 1] = { */
'\x00', '\x0b', '\x0e', '\x24', '\x56', '\x56', '\x5a',
/* }; */
/* static const char item_offset[90] = { */
'\x00', '\x02', '\x04', '\x06', '\x08', '\x0a', '\x0c', '\x0e',
'\x10', '\x12', '\x14', '\x1a', '\x1b', '\x1b', '\x1b', '\x1b',
'\x1b', '\x1b', '\x1b', '\x1b', '\x1b', '\x1c', '\x1c', '\x1c',
'\x1c', '\x1c', '\x1c', '\x1c', '\x1c', '\x1c', '\x1c', '\x1c',
'\x1c', '\x1c', '\x1c', '\x1e', '\x20', '\x24', '\x28', '\x2c',
'\x30', '\x34', '\x38', '\x3c', '\x43', '\x4a', '\x52', '\x5c',
'\x65', '\x6c', '\x75', '\x79', '\x7d', '\x81', '\x85', '\x89',
'\x8d', '\x91', '\x95', '\x99', '\x9d', '\xa1', '\xa5', '\xad',
'\x36', '\x3c', '\x42', '\x46', '\x4b', '\x50', '\x57', '\x61',
'\x69', '\x72', '\x7b', '\x7e', '\x81', '\x96', '\x9f', '\xa8',
'\xb3', '\xb3', '\xb3', '\xb3', '\xb3', '\xb3', '\xb4', '\xba',
'\xbf', '\xbf',
/* }; */
/* static const char C_locale_data[320] = { */
'0', '\x00', '1', '\x00', '2', '\x00', '3', '\x00',
'4', '\x00', '5', '\x00', '6', '\x00', '7', '\x00',
'8', '\x00', '9', '\x00', 'A', 'S', 'C', 'I',
'I', '\x00', '.', '\x00', '\x7f', '\x00', '-', '\x00',
'S', 'u', 'n', '\x00', 'M', 'o', 'n', '\x00',
'T', 'u', 'e', '\x00', 'W', 'e', 'd', '\x00',
'T', 'h', 'u', '\x00', 'F', 'r', 'i', '\x00',
'S', 'a', 't', '\x00', 'S', 'u', 'n', 'd',
'a', 'y', '\x00', 'M', 'o', 'n', 'd', 'a',
'y', '\x00', 'T', 'u', 'e', 's', 'd', 'a',
'y', '\x00', 'W', 'e', 'd', 'n', 'e', 's',
'd', 'a', 'y', '\x00', 'T', 'h', 'u', 'r',
's', 'd', 'a', 'y', '\x00', 'F', 'r', 'i',
'd', 'a', 'y', '\x00', 'S', 'a', 't', 'u',
'r', 'd', 'a', 'y', '\x00', 'J', 'a', 'n',
'\x00', 'F', 'e', 'b', '\x00', 'M', 'a', 'r',
'\x00', 'A', 'p', 'r', '\x00', 'M', 'a', 'y',
'\x00', 'J', 'u', 'n', '\x00', 'J', 'u', 'l',
'\x00', 'A', 'u', 'g', '\x00', 'S', 'e', 'p',
'\x00', 'O', 'c', 't', '\x00', 'N', 'o', 'v',
'\x00', 'D', 'e', 'c', '\x00', 'J', 'a', 'n',
'u', 'a', 'r', 'y', '\x00', 'F', 'e', 'b',
'r', 'u', 'a', 'r', 'y', '\x00', 'M', 'a',
'r', 'c', 'h', '\x00', 'A', 'p', 'r', 'i',
'l', '\x00', 'M', 'a', 'y', '\x00', 'J', 'u',
'n', 'e', '\x00', 'J', 'u', 'l', 'y', '\x00',
'A', 'u', 'g', 'u', 's', 't', '\x00', 'S',
'e', 'p', 't', 'e', 'm', 'b', 'e', 'r',
'\x00', 'O', 'c', 't', 'o', 'b', 'e', 'r',
'\x00', 'N', 'o', 'v', 'e', 'm', 'b', 'e',
'r', '\x00', 'D', 'e', 'c', 'e', 'm', 'b',
'e', 'r', '\x00', 'A', 'M', '\x00', 'P', 'M',
'\x00', '%', 'a', ' ', '%', 'b', ' ', '%',
'e', ' ', '%', 'H', ':', '%', 'M', ':',
'%', 'S', ' ', '%', 'Y', '\x00', '%', 'm',
'/', '%', 'd', '/', '%', 'y', '\x00', '%',
'H', ':', '%', 'M', ':', '%', 'S', '\x00',
'%', 'I', ':', '%', 'M', ':', '%', 'S',
' ', '%', 'p', '\x00', '^', '[', 'y', 'Y',
']', '\x00', '^', '[', 'n', 'N', ']', '\x00',
};
char *nl_langinfo(nl_item item)
{
unsigned int c;
unsigned int i;
if ((c = _NL_ITEM_CATEGORY(item)) < C_LC_ALL) {
if ((i = cat_start[c] + _NL_ITEM_INDEX(item)) < cat_start[c+1]) {
/* return (char *) C_locale_data + item_offset[i] + (i & 64); */
return (char *) C_locale_data + nl_data[C_LC_ALL+1+i] + 2*(i & 64);
}
}
return (char *) cat_start; /* Conveniently, this is the empty string. */
}
libc_hidden_def(nl_langinfo)
#else /* __LOCALE_C_ONLY */
#if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE)
char *nl_langinfo(nl_item item)
{
return nl_langinfo_l(item, __UCLIBC_CURLOCALE);
}
libc_hidden_def(nl_langinfo)
#else /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
libc_hidden_proto(__XL_NPP(nl_langinfo))
static const char empty[] = "";
char *__XL_NPP(nl_langinfo)(nl_item item __LOCALE_PARAM )
{
unsigned int c = _NL_ITEM_CATEGORY(item);
unsigned int i = _NL_ITEM_INDEX(item);
if ((c < LC_ALL) && (i < __LOCALE_PTR->category_item_count[c])) {
return ((char **)(((char *) __LOCALE_PTR)
+ __LOCALE_PTR->category_offsets[c]))[i];
}
return (char *) empty;
}
libc_hidden_def(__XL_NPP(nl_langinfo))
#endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
#endif /* __LOCALE_C_ONLY */
#endif
/**********************************************************************/
#ifdef L_newlocale
static const char posix[] = "POSIX";
static const char utf8[] = "UTF-8";
static int find_locale(int category_mask, const char *p,
unsigned char *new_locale)
{
int i;
const unsigned char *s;
uint16_t n;
unsigned char lang_cult, codeset;
#if defined(__LOCALE_DATA_AT_MODIFIERS_LENGTH) && 1
/* Support standard locale handling for @-modifiers. */
char buf[18]; /* TODO: 7+{max codeset name length} */
const char *q;
if ((q = strchr(p,'@')) != NULL) {
if ((((size_t)((q-p)-5)) > (sizeof(buf) - 5)) || (p[2] != '_')) {
return 0;
}
/* locale name at least 5 chars long and 3rd char is '_' */
s = LOCALE_AT_MODIFIERS;
do {
if (!strcmp((char*) (s + 2), q + 1)) {
break;
}
s += 2 + *s; /* TODO - fix this throughout */
} while (*s);
if (!*s) {
return 0;
}
assert(q - p < sizeof(buf));
memcpy(buf, p, q-p);
buf[q-p] = 0;
buf[2] = s[1];
p = buf;
}
#endif
lang_cult = codeset = 0; /* Assume C and default codeset. */
if (((*p == 'C') && !p[1]) || !strcmp(p, posix)) {
goto FIND_LOCALE;
}
if ((strlen(p) > 5) && (p[5] == '.')) { /* Codeset in locale name? */
/* TODO: maybe CODESET_LIST + *s ??? */
/* 7bit is 1, UTF-8 is 2, 8-bit is >= 3 */
codeset = 2;
if (strcasecmp(utf8, p + 6) != 0) {/* TODO - fix! */
s = CODESET_LIST;
do {
++codeset; /* Increment codeset first. */
if (!strcmp((char*) CODESET_LIST + *s, p + 6)) {
goto FIND_LANG_CULT;
}
} while (*++s);
return 0; /* No matching codeset! */
}
}
FIND_LANG_CULT: /* Find language_culture number. */
s = LOCALE_NAMES;
do { /* TODO -- do a binary search? */
/* TODO -- fix gen_mmap!*/
++lang_cult; /* Increment first since C/POSIX is 0. */
if (!strncmp((char*) s, p, 5)) { /* Found a matching locale name; */
goto FIND_LOCALE;
}
s += 5;
} while (lang_cult < __LOCALE_DATA_NUM_LOCALE_NAMES);
return 0; /* No matching language_culture! */
FIND_LOCALE: /* Find locale row matching name and codeset */
s = LOCALES;
n = 0;
do { /* TODO -- do a binary search? */
if ((lang_cult == *s) && ((codeset == s[1]) || (codeset == s[2]))) {
i = 1;
s = new_locale + 1;
do {
if (category_mask & i) {
/* Encode current locale row number. */
((unsigned char *) s)[0] = (n >> 7) | 0x80;
((unsigned char *) s)[1] = (n & 0x7f) | 0x80;
}
s += 2;
i += i;
} while (i < (1 << LC_ALL));
return i; /* Return non-zero */
}
s += __LOCALE_DATA_WIDTH_LOCALES;
++n;
} while (n <= __LOCALE_DATA_NUM_LOCALES); /* We started at 1!!! */
return 0; /* Unsupported locale. */
}
static unsigned char *composite_locale(int category_mask, const char *locale,
unsigned char *new_locale)
{
char buf[MAX_LOCALE_STR];
char *t;
char *e;
int c;
int component_mask;
if (!strchr(locale,'=')) {
if (!find_locale(category_mask, locale, new_locale)) {
return NULL;
}
return new_locale;
}
if (strlen(locale) >= sizeof(buf)) {
return NULL;
}
stpcpy(buf, locale);
component_mask = 0;
t = strtok_r(buf, "=", &e); /* This can't fail because of strchr test above. */
do {
c = 0;
/* CATEGORY_NAMES is unsigned char* */
while (strcmp((char*) CATEGORY_NAMES + (int) CATEGORY_NAMES[c], t)) {
if (++c == LC_ALL) { /* Unknown category name! */
return NULL;
}
}
t = strtok_r(NULL, ";", &e);
c = (1 << c);
if (component_mask & c) { /* Multiple components for one category. */
return NULL;
}
component_mask |= c;
if ((category_mask & c) && (!t || !find_locale(c, t, new_locale))) {
return NULL;
}
} while ((t = strtok_r(NULL, "=", &e)) != NULL);
if (category_mask & ~component_mask) { /* Category component(s) missing. */
return NULL;
}
return new_locale;
}
__locale_t newlocale(int category_mask, const char *locale, __locale_t base)
{
const char *p;
int i, j, k;
unsigned char new_selector[LOCALE_SELECTOR_SIZE];
if (category_mask == (1 << LC_ALL)) {
category_mask = LC_ALL_MASK;
}
if (!locale || ((unsigned)(category_mask) > LC_ALL_MASK)) {
INVALID:
__set_errno(EINVAL);
return NULL; /* No locale or illegal/unsupported category. */
}
strcpy((char *) new_selector,
(base ? (char *) base->cur_locale : C_LOCALE_SELECTOR));
if (!locale[0]) { /* locale == "", so check environment. */
const char *envstr[4];
envstr[0] = "LC_ALL";
envstr[1] = NULL;
envstr[2] = "LANG";
envstr[3] = posix;
i = 1;
k = 0;
do {
if (category_mask & i) {
/* Note: SUSv3 doesn't define a fallback mechanism here.
* So, if LC_ALL is invalid, we do _not_ continue trying
* the other environment vars. */
envstr[1] = (char*) CATEGORY_NAMES + CATEGORY_NAMES[k];
j = 0;
while (1) {
p = envstr[j];
if (++j >= 4)
break; /* now p == "POSIX" */
p = getenv(p);
if (p && p[0])
break;
};
/* The user set something... is it valid? */
/* Note: Since we don't support user-supplied locales and
* alternate paths, we don't need to worry about special
* handling for suid/sgid apps. */
if (!find_locale(i, p, new_selector)) {
goto INVALID;
}
}
i += i;
} while (++k < LC_ALL);
} else if (!composite_locale(category_mask, locale, new_selector)) {
goto INVALID;
}
/* If we get here, the new selector corresponds to a valid locale. */
#if 0
if (base) {
_locale_set_l(new_selector, base);
} else {
base = _locale_new(new_selector);
}
#else
if (!base) {
base = calloc(1, sizeof(struct __uclibc_locale_struct));
if (base == NULL)
return base;
_locale_init_l(base);
}
_locale_set_l(new_selector, base);
#endif
return base;
}
#ifdef __UCLIBC_HAS_XLOCALE__
libc_hidden_def(newlocale)
#endif
#endif
/**********************************************************************/
#ifdef L_duplocale
__locale_t duplocale(__locale_t dataset)
{
__locale_t r;
uint16_t * i2w;
size_t n;
assert(dataset != LC_GLOBAL_LOCALE);
r = malloc(sizeof(struct __uclibc_locale_struct));
if (r != NULL) {
n = 2 * dataset->collate.max_col_index + 2;
i2w = calloc(n, sizeof(uint16_t));
if (i2w != NULL) {
memcpy(r, dataset, sizeof(struct __uclibc_locale_struct));
r->collate.index2weight = i2w;
memcpy(i2w, dataset->collate.index2weight, n * sizeof(uint16_t));
} else {
free(r);
r = NULL;
}
}
return r;
}
#endif
/**********************************************************************/
#ifdef L_freelocale
void freelocale(__locale_t dataset)
{
assert(dataset != __global_locale);
assert(dataset != LC_GLOBAL_LOCALE);
free(dataset->collate.index2weight); /* Free collation data. */
free(dataset); /* Free locale */
}
#endif
/**********************************************************************/
#ifdef L_uselocale
__locale_t uselocale(__locale_t dataset)
{
__locale_t old;
if (!dataset) {
old = __UCLIBC_CURLOCALE;
} else {
if (dataset == LC_GLOBAL_LOCALE) {
dataset = __global_locale;
}
#ifdef __UCLIBC_HAS_THREADS__
old = __curlocale_set(dataset);
#else
old = __curlocale_var;
__curlocale_var = dataset;
#endif
}
if (old == __global_locale) {
return LC_GLOBAL_LOCALE;
}
return old;
}
libc_hidden_def(uselocale)
#endif
/**********************************************************************/
#ifdef L___curlocale
#ifdef __UCLIBC_HAS_THREADS__
__locale_t weak_const_function __curlocale(void)
{
return __curlocale_var; /* This is overriden by the thread version. */
}
libc_hidden_weak(__curlocale)
__locale_t weak_function __curlocale_set(__locale_t newloc)
{
__locale_t oldloc = __curlocale_var;
assert(newloc != LC_GLOBAL_LOCALE);
__curlocale_var = newloc;
return oldloc;
}
libc_hidden_weak(__curlocale_set)
#endif
#endif
/**********************************************************************/
#ifdef L___locale_mbrtowc_l
/* NOTE: This returns an int... not size_t. Also, it is not a general
* routine. It is actually a very stripped-down version of mbrtowc
* that takes a __locale_t arg. This is used by strcoll and strxfrm.
* It is also used above to generate wchar_t versions of the decimal point
* and thousands seperator. */
#ifndef __CTYPE_HAS_UTF_8_LOCALES
#warning __CTYPE_HAS_UTF_8_LOCALES not set!
#endif
#ifndef __CTYPE_HAS_8_BIT_LOCALES
#warning __CTYPE_HAS_8_BIT_LOCALES not set!
#endif
#define Cc2wc_IDX_SHIFT __LOCALE_DATA_Cc2wc_IDX_SHIFT
#define Cc2wc_ROW_LEN __LOCALE_DATA_Cc2wc_ROW_LEN
extern size_t _wchar_utf8sntowcs(wchar_t *__restrict pwc, size_t wn,
const char **__restrict src, size_t n,
mbstate_t *ps, int allow_continuation) attribute_hidden;
int attribute_hidden __locale_mbrtowc_l(wchar_t *__restrict dst,
const char *__restrict src,
__locale_t loc )
{
#ifdef __CTYPE_HAS_UTF_8_LOCALES
if (loc->encoding == __ctype_encoding_utf8) {
mbstate_t ps;
const char *p = src;
size_t r;
ps.__mask = 0;
r = _wchar_utf8sntowcs(dst, 1, &p, SIZE_MAX, &ps, 1);
return (r == 1) ? (p-src) : r; /* Need to return 0 if nul char. */
}
#endif
#ifdef __CTYPE_HAS_8_BIT_LOCALES
assert((loc->encoding == __ctype_encoding_7_bit) || (loc->encoding == __ctype_encoding_8_bit));
#else
assert(loc->encoding == __ctype_encoding_7_bit);
#endif
if ((*dst = ((unsigned char)(*src))) < 0x80) { /* ASCII... */
return (*src != 0);
}
#ifdef __CTYPE_HAS_8_BIT_LOCALES
if (loc->encoding == __ctype_encoding_8_bit) {
wchar_t wc = *dst - 0x80;
*dst = loc->tbl8c2wc[
(loc->idx8c2wc[wc >> Cc2wc_IDX_SHIFT]
<< Cc2wc_IDX_SHIFT) + (wc & (Cc2wc_ROW_LEN - 1))];
if (*dst) {
return 1;
}
}
#endif
return -1;
}
#endif
/**********************************************************************/